From aeb95010d35a1be01e9306147948d0ad4184489e Mon Sep 17 00:00:00 2001 From: Daveo Date: Fri, 12 Jan 2001 22:40:39 +0000 Subject: [PATCH] --- Utils/GinExp/ASCIIEXP.DSW | 37 ++ Utils/GinExp/ASCIIEXP.opt | Bin 0 -> 62976 bytes Utils/GinExp/ExpAnimKeys.cpp | 753 ++++++++++++++++++++++++++ Utils/GinExp/ExpBone.cpp | 991 +++++++++++++++++++++++++++++++++++ Utils/GinExp/ExpCamLight.cpp | 237 +++++++++ Utils/GinExp/ExpFFD.cpp | 550 +++++++++++++++++++ Utils/GinExp/ExpFileIo.cpp | 262 +++++++++ Utils/GinExp/ExpFileIo.h | 63 +++ Utils/GinExp/ExpMain.cpp | 741 ++++++++++++++++++++++++++ Utils/GinExp/ExpMaterial.cpp | 518 ++++++++++++++++++ Utils/GinExp/ExpModel.cpp | 952 +++++++++++++++++++++++++++++++++ Utils/GinExp/ExpTree.cpp | 536 +++++++++++++++++++ Utils/GinExp/ExpUProp.cpp | 96 ++++ Utils/GinExp/Triobjed.h | 958 +++++++++++++++++++++++++++++++++ Utils/GinExp/asciiexp.aps | Bin 0 -> 35636 bytes Utils/GinExp/asciiexp.def | 8 + Utils/GinExp/asciiexp.dsp | 201 +++++++ Utils/GinExp/asciiexp.h | 456 ++++++++++++++++ Utils/GinExp/asciiexp.ncb | Bin 0 -> 181248 bytes Utils/GinExp/asciiexp.rc | 174 ++++++ Utils/GinExp/ffd.h | 104 ++++ Utils/GinExp/resource.h | 68 +++ source/player/player.cpp | 39 +- source/player/player.h | 11 +- 24 files changed, 7743 insertions(+), 12 deletions(-) create mode 100644 Utils/GinExp/ASCIIEXP.DSW create mode 100644 Utils/GinExp/ASCIIEXP.opt create mode 100644 Utils/GinExp/ExpAnimKeys.cpp create mode 100644 Utils/GinExp/ExpBone.cpp create mode 100644 Utils/GinExp/ExpCamLight.cpp create mode 100644 Utils/GinExp/ExpFFD.cpp create mode 100644 Utils/GinExp/ExpFileIo.cpp create mode 100644 Utils/GinExp/ExpFileIo.h create mode 100644 Utils/GinExp/ExpMain.cpp create mode 100644 Utils/GinExp/ExpMaterial.cpp create mode 100644 Utils/GinExp/ExpModel.cpp create mode 100644 Utils/GinExp/ExpTree.cpp create mode 100644 Utils/GinExp/ExpUProp.cpp create mode 100644 Utils/GinExp/Triobjed.h create mode 100644 Utils/GinExp/asciiexp.aps create mode 100644 Utils/GinExp/asciiexp.def create mode 100644 Utils/GinExp/asciiexp.dsp create mode 100644 Utils/GinExp/asciiexp.h create mode 100644 Utils/GinExp/asciiexp.ncb create mode 100644 Utils/GinExp/asciiexp.rc create mode 100644 Utils/GinExp/ffd.h create mode 100644 Utils/GinExp/resource.h diff --git a/Utils/GinExp/ASCIIEXP.DSW b/Utils/GinExp/ASCIIEXP.DSW new file mode 100644 index 000000000..d5b4bf918 --- /dev/null +++ b/Utils/GinExp/ASCIIEXP.DSW @@ -0,0 +1,37 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "asciiexp"=.\asciiexp.dsp - Package Owner=<4> + +Package=<5> +{{{ + begin source code control + "$/TP2psx/utils/GinExp", ZDXAAAAA + . + end source code control +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ + begin source code control + "$/TP2psx/utils/GinExp", HFXAAAAA + . + end source code control +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/Utils/GinExp/ASCIIEXP.opt b/Utils/GinExp/ASCIIEXP.opt new file mode 100644 index 0000000000000000000000000000000000000000..66eb4776f0707a04cc74abe98b80d7fb529bffb2 GIT binary patch literal 62976 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 literal 0 HcmV?d00001 diff --git a/Utils/GinExp/ExpAnimKeys.cpp b/Utils/GinExp/ExpAnimKeys.cpp new file mode 100644 index 000000000..3403fae30 --- /dev/null +++ b/Utils/GinExp/ExpAnimKeys.cpp @@ -0,0 +1,753 @@ +//************************************************************************** +//* Animout.cpp - Ascii File Exporter +//* +//* By Christer Janson +//* Kinetix Development +//* +//* January 20, 1997 CCJ Initial coding +//* +//* This module handles controller key output and controller sampling. +//* +//* Copyright (c) 1997, All Rights Reserved. +//*************************************************************************** + +#include "asciiexp.h" + +#define ALMOST_ZERO 1.0e-3f +BOOL EqualPoint3(Point3 p1, Point3 p2); + +/**************************************************************************** + + TM Animation output + +****************************************************************************/ + +// Get hold of the transform controllers for the node... +void AsciiExp::ExportAnimKeys( INode* node ) +{ + BOOL bPosAnim; + BOOL bRotAnim; + BOOL bScaleAnim; + BOOL bDoKeys = FALSE; + + // We can only export keys if all TM controllers are "known" to us. + // The reason for that is that some controllers control more than what + // they should. Consider a path position controller, if you turn on + // follow and banking, this position controller will also control + // rotation. If a node that had a path position controller also had a + // TCB rotation controller, the TCB keys would not describe the whole + // rotation of the node. + // For that reason we will only export keys if all controllers + // position, rotation and scale are linear, hybrid (bezier) or tcb. + +/* if (!GetAlwaysSample()) + { + Control* pC = node->GetTMController()->GetPositionController(); + Control* rC = node->GetTMController()->GetRotationController(); + Control* sC = node->GetTMController()->GetScaleController(); + + if (IsKnownController(pC) && IsKnownController(rC) && IsKnownController(sC)) + { + bDoKeys = TRUE; + } + } +*/ + if (bDoKeys) + { + // Only dump the track header if any of the controllers have keys + if (node->GetTMController()->GetPositionController()->NumKeys() || + node->GetTMController()->GetRotationController()->NumKeys() || + node->GetTMController()->GetScaleController()->NumKeys()) + { +// fprintf(pStream,"%s\t%s {\n", indent.data(), ID_TM_ANIMATION); +// fprintf(pStream,"%s\t\t%s \"%s\"\n", indent.data(), ID_NODE_NAME, FixupName(node->GetName())); + + DumpPosKeys(node->GetTMController()->GetPositionController()); + DumpRotKeys(node->GetTMController()->GetRotationController()); + DumpScaleKeys(node->GetTMController()->GetScaleController()); + +// fprintf(pStream,"%s\t}\n", indent.data()); + } + } + else if (CheckForAnimation(node, bPosAnim, bRotAnim, bScaleAnim)) + { +// fprintf(pStream,"%s\t%s {\n", indent.data(), ID_TM_ANIMATION); +// fprintf(pStream,"%s\t\t%s \"%s\"\n", indent.data(), ID_NODE_NAME, FixupName(node->GetName())); + + if (bPosAnim) DumpPosSample(node); + if (bRotAnim) DumpRotSample(node); + if (bScaleAnim) DumpScaleSample(node); + +// fprintf(pStream,"%s\t}\n", indent.data()); + } +} + + +// To really see if a node is animated we can step through the animation range +// and decompose the TM matrix for every frame and examine the components. +// This way we can identify position, rotation and scale animation separately. +// +// Some controllers makes it problematic to examine the TMContollers instead of +// the actual TMMatrix. For example, a path controller is a position controller, +// but if you turn on follow and banking, it will also affect the rotation component. +// If we want to, we can examine the position, rotation and scale controllers and +// if they all are Linear, Hybrid (bezier) or TCB, then we could export the actual keys. +// This is not at all difficult, but the importer has to know the exact interpolation +// algorithm in order to use it. The source code to the interpolation routines are available +// to ADN members. +// +// For an example of how to export actual keys, look at DumpPoint3Keys() below. +// This method will check the actual controller to determine if the controller is known. +// If we know how to work this controller, its actual keys will be exported, +// otherwise the controller will be sampled using the user specified sampling frequency. + +BOOL AsciiExp::CheckForAnimation(INode* node, BOOL& bPos, BOOL& bRot, BOOL& bScale) +{ + TimeValue start = ip->GetAnimRange().Start(); + TimeValue end = ip->GetAnimRange().End(); + TimeValue t; + int delta = GetTicksPerFrame(); + Matrix3 tm; + AffineParts ap; + Point3 firstPos; + float rotAngle, firstRotAngle; + Point3 rotAxis; + Point3 firstScaleFactor; + + bPos = bRot = bScale = FALSE; + + for (t=start; t<=end; t+=delta) + { + tm = node->GetNodeTM(t) * Inverse(node->GetParentTM(t)); + + decomp_affine(tm, &ap); + + AngAxisFromQ(ap.q, &rotAngle, rotAxis); + + if (t != start) + { + // We examine the rotation angle to see if the rotation component + // has changed. + // Although not entierly true, it should work. + // It is rare that the rotation axis is animated without + // the rotation angle being somewhat affected. + bPos = TRUE; + bRot = TRUE; + bScale = TRUE; +// if (!EqualPoint3(ap.t, firstPos)) bPos = TRUE; +// if (fabs(rotAngle - firstRotAngle) > ALMOST_ZERO) bRot = TRUE; +// if (!EqualPoint3(ap.k, firstScaleFactor)) bScale = TRUE; + } + else + { + firstPos = ap.t; + firstRotAngle = rotAngle; + firstScaleFactor = ap.k; + } + + // No need to continue looping if all components are animated + if (bPos && bRot && bScale) break; + } + + return bPos || bRot || bScale; +} + + +void AsciiExp::DumpPosQuatSample(INode* node) +{ +// fprintf(pStream,"%s\t\t%s {\n", indent.data(), ID_POS_TRACK); + + TimeValue start = ip->GetAnimRange().Start(); + TimeValue end = ip->GetAnimRange().End(); + TimeValue t; + int delta = GetTicksPerFrame(); + Matrix3 tm; + AffineParts ap; + Point3 prevPos; + Quat prevQ; + Quat q; + char name[256]; + + + sprintf( name, "%s", node->GetName() ); + fprintf( tempStream, "OUTPUTTING - %s\n", name ); // DEBUG FILE +// fwrite( &name, sizeof(char), NAME_LENGTH, boneStream ); // WRITE BONE NAME + + prevQ.Identity(); + + for (t=start; t<=end; t+=delta) + { + // TRANSLATION + tm = node->GetNodeTM(t);// * Inverse(node->GetParentTM(t)); + decomp_affine(tm, &ap); + + fwrite( &ap.t.x, sizeof(float), 1, expStream ); // WRITE BONE X-POS + fwrite( &ap.t.z, sizeof(float), 1, expStream ); // WRITE BONE Y-POS + fwrite( &ap.t.y, sizeof(float), 1, expStream ); // WRITE BONE Z-POS + + fwrite( &ap.q.x, sizeof(float), 1, expStream ); // WRITE BONE ROT X-AXIS + fwrite( &ap.q.z, sizeof(float), 1, expStream ); // WRITE BONE ROT Y-AXIS + fwrite( &ap.q.y, sizeof(float), 1, expStream ); // WRITE BONE ROT Z-AXIS + fwrite( &ap.q.w, sizeof(float), 1, expStream ); // WRITE BONE ROT W ANGLE + } +} + +void AsciiExp::DumpPosSample(INode* node) +{ +// fprintf(pStream,"%s\t\t%s {\n", indent.data(), ID_POS_TRACK); + + TimeValue start = ip->GetAnimRange().Start(); + TimeValue end = ip->GetAnimRange().End(); + TimeValue t; + int delta = GetTicksPerFrame(); + Matrix3 tm; + AffineParts ap; + Point3 prevPos; + Quat prevQ; + Quat q; + char name[256]; + + + sprintf( name, "%s", node->GetName() ); + fprintf( tempStream, "OUTPUTTING - %s\n", name ); // DEBUG FILE +// fwrite( &name, sizeof(char), NAME_LENGTH, boneStream ); // WRITE BONE NAME + + prevQ.Identity(); + + for (t=start; t<=end; t+=delta) + { + // TRANSLATION + tm = node->GetNodeTM(t);// * Inverse(node->GetParentTM(t)); + decomp_affine(tm, &ap); + + Point3 fpos; + Point3 pos = ap.t; + + prevPos = pos; + + fpos.x = pos.x; + fpos.y = pos.z; + fpos.z = -pos.y; + + fprintf( tempStream, " POS = %f %f %f\n", pos.x, pos.z, -pos.y ); +// fwrite( &fpos.x, sizeof(float), 1, boneStream ); // WRITE BONE X-POS +// fwrite( &fpos.y, sizeof(float), 1, boneStream ); // WRITE BONE Y-POS +// fwrite( &fpos.z, sizeof(float), 1, boneStream ); // WRITE BONE Z-POS + + + // ROTATION + tm = node->GetNodeTM(t);// * Inverse(node->GetParentTM(t)); + + decomp_affine(tm, &ap); + + if (t == start) + { + q = ap.q; + prevQ = ap.q; // SAVE BASE ROTATION, THEN USE THIS AS ROTATION ORIGIN + } + else + { + q = ap.q / prevQ; + } + + fprintf( tempStream, " QUAT = %f %f %f %f\n", q.x, q.y, q.z, q.w ); +// fwrite( &q.x, sizeof(float), 1, boneStream ); // WRITE BONE ROT X-AXIS +// fwrite( &q.y, sizeof(float), 1, boneStream ); // WRITE BONE ROT Y-AXIS +// fwrite( &q.z, sizeof(float), 1, boneStream ); // WRITE BONE ROT Z-AXIS +// fwrite( &q.w, sizeof(float), 1, boneStream ); // WRITE BONE ROT W ANGLE + } +} + + +void AsciiExp::DumpRotSample(INode* node) +{ +// fprintf(pStream,"%s\t\t%s {\n", indent.data(), ID_ROT_TRACK); + + TimeValue start = ip->GetAnimRange().Start(); + TimeValue end = ip->GetAnimRange().End(); + TimeValue t; + int delta = GetTicksPerFrame(); + Matrix3 tm; + AffineParts ap; + Quat prevQ; + + prevQ.Identity(); + + for (t=start; t<=end; t+=delta) + { + tm = node->GetNodeTM(t) * Inverse(node->GetParentTM(t)); + + decomp_affine(tm, &ap); + + // Rotation keys should be relative, so we need to convert these + // absolute samples to relative values. + + Quat q = ap.q / prevQ; + prevQ = ap.q; + + // No point in exporting null keys... +// if (q.IsIdentity()) continue; + + // Output the sample + fprintf( tempStream, " QUAT = %f %f %f %f\n", q.x, q.y, q.z, q.w ); +// fprintf(pStream, "%s\t\t\t%s %d\t%s\n", indent.data(), ID_ROT_SAMPLE, t, Format(q)); + } + +// fprintf(pStream,"%s\t\t}\n", indent.data()); +} + + +void AsciiExp::DumpScaleSample(INode* node) +{ +// fprintf(pStream,"%s\t\t%s {\n", indent.data(), ID_SCALE_TRACK); + + TimeValue start = ip->GetAnimRange().Start(); + TimeValue end = ip->GetAnimRange().End(); + TimeValue t; + int delta = GetTicksPerFrame(); + Matrix3 tm; + AffineParts ap; + Point3 prevFac; + + for (t=start; t<=end; t+=delta) + { + tm = node->GetNodeTM(t) * Inverse(node->GetParentTM(t)); + decomp_affine(tm, &ap); + + // Skip identical keys + if (t!= start && EqualPoint3(ap.k, prevFac)) continue; + + prevFac = ap.k; + + // Output the sample +// fprintf(pStream, "%s\t\t\t%s %d\t%s %s\n", indent.data(), ID_SCALE_SAMPLE, t, Format(ap.k), Format(ap.u)); + } + +// fprintf(pStream,"%s\t\t}\n", indent.data()); +} + + +// Output point3 keys if this is a known point3 controller that +// supports key operations. Otherwise we will sample the controller +// once for each frame to get the value. +// Point3 controllers can control, for example, color. +void AsciiExp::DumpPoint3Keys(Control* cont) +{ + // Bug out if no controller. + if (!cont) return; + + int i; + IKeyControl *ikc = NULL; + + // If the user wants us to always sample, we will ignore the KeyControlInterface +// if (!GetAlwaysSample()) ikc = GetKeyControlInterface(cont); + + // TCB point3 + if (ikc && cont->ClassID() == Class_ID(TCBINTERP_POINT3_CLASS_ID, 0)) + { +// fprintf(pStream,"%s\t\t%s {\n", indent.data(), ID_CONTROL_POINT3_TCB); + for (i=0; iGetNumKeys(); i++) + { + ITCBPoint3Key key; + ikc->GetKey(i, &key); + fprintf( tempStream, " KEYPOS = %f %f %f\n", key.val.x, key.val.y, key.val.z ); +// fprintf(pStream, "%s\t\t\t%s %d\t%s", indent.data(), ID_TCB_POINT3_KEY, key.time, Format(key.val)); + // Add TCB specific data +// fprintf(pStream, "\t%s\t%s\t%s\t%s\t%s\n", Format(key.tens), Format(key.cont), Format(key.bias), Format(key.easeIn), Format(key.easeOut)); + } +// fprintf(pStream,"%s\t\t}\n", indent.data()); + } + // Bezier point3 + else if (ikc && cont->ClassID() == Class_ID(HYBRIDINTERP_POINT3_CLASS_ID, 0)) + { +// fprintf(pStream,"%s\t\t%s {\n", indent.data(), ID_CONTROL_POINT3_BEZIER); + for (i=0; iGetNumKeys(); i++) + { + IBezPoint3Key key; + ikc->GetKey(i, &key); + fprintf( tempStream, " KEYPOS = %f %f %f\n", key.val.x, key.val.y, key.val.z ); +// fprintf(pStream, "%s\t\t\t%s %d\t%s", indent.data(), ID_BEZIER_POINT3_KEY, key.time, Format(key.val)); +// fprintf(pStream, "\t%s\t%s\t%d\n", Format(key.intan), Format(key.outtan), key.flags); + } +// fprintf(pStream,"%s\t\t}\n", indent.data()); + } + // Bezier color + else if (ikc && cont->ClassID() == Class_ID(HYBRIDINTERP_COLOR_CLASS_ID, 0)) + { +// fprintf(pStream,"%s\t\t%s {\n", indent.data(), ID_CONTROL_COLOR_BEZIER); + for (i=0; iGetNumKeys(); i++) + { + IBezPoint3Key key; + ikc->GetKey(i, &key); + fprintf( tempStream, " KEYPOS = %f %f %f\n", key.val.x, key.val.y, key.val.z ); +// fprintf(pStream, "%s\t\t\t%s %d\t%s", indent.data(), ID_BEZIER_POINT3_KEY, key.time, Format(key.val)); +// fprintf(pStream, "\t%s\t%s\t%d\n", Format(key.intan), Format(key.outtan), key.flags); + } +// fprintf(pStream,"%s\t\t}\n", indent.data()); + } + else + { + // Unknown controller, no key interface or sample on demand - + // This might be a procedural controller or something else we + // don't know about. The last resort is to get the value from the + // controller at every n frames. + + TSTR name; + cont->GetClassName(name); +// fprintf(pStream,"%s\t\t%s \"%s\" {\n", indent.data(), ID_CONTROL_POINT3_SAMPLE, FixupName(name)); + + // If it is animated at all... + if (cont->IsAnimated()) + { + // Get the range of the controller animation + Interval range; + // Get range of full animation + Interval animRange = ip->GetAnimRange(); + TimeValue t = cont->GetTimeRange(TIMERANGE_ALL).Start(); + Point3 value; + + // While we are inside the animation... + while (animRange.InInterval(t)) + { + // Sample the controller + range = FOREVER; + cont->GetValue(t, &value, range); + + // Set time to start of controller validity interval + t = range.Start(); + + // Output the sample + fprintf( tempStream, " KEYPOS = %f %f %f\n", value.x, value.y, value.z ); +// fprintf(pStream, "%s\t\t\t%s %d\t%s\n", indent.data(), ID_POINT3_KEY, t, Format(value)); + + // If the end of the controller validity is beyond the + // range of the animation + if (range.End() > cont->GetTimeRange(TIMERANGE_ALL).End()) + { + break; + } + else + { + t = (range.End()/GetTicksPerFrame()+1) * GetTicksPerFrame(); + } + } + } +// fprintf(pStream,"%s\t\t}\n", indent.data()); + } +} + + +// Output float keys if this is a known float controller that +// supports key operations. Otherwise we will sample the controller +// once for each frame to get the value. +void AsciiExp::DumpFloatKeys(Control* cont) +{ + if (!cont) return; + + int i; + IKeyControl *ikc = NULL; + + // If the user wants us to always sample, we will ignore the KeyControlInterface +// if (!GetAlwaysSample()) ikc = GetKeyControlInterface(cont); + + // TCB float + if (ikc && cont->ClassID() == Class_ID(TCBINTERP_FLOAT_CLASS_ID, 0)) + { +// fprintf(pStream,"%s\t\t%s {\n", indent.data(), ID_CONTROL_FLOAT_TCB); + for (i=0; iGetNumKeys(); i++) + { + ITCBFloatKey key; + ikc->GetKey(i, &key); +// fprintf(pStream, "%s\t\t\t%s %d\t%s", indent.data(), ID_TCB_FLOAT_KEY, key.time, Format(key.val)); +// fprintf(pStream, "\t%s\t%s\t%s\t%s\t%s\n", Format(key.tens), Format(key.cont), Format(key.bias), Format(key.easeIn), Format(key.easeOut)); + } +// fprintf(pStream,"%s\t\t}\n", indent.data()); + } + // Bezier float + else if (ikc && cont->ClassID() == Class_ID(HYBRIDINTERP_FLOAT_CLASS_ID, 0)) + { +// fprintf(pStream,"%s\t\t%s {\n", indent.data(), ID_CONTROL_FLOAT_BEZIER); + for (i=0; iGetNumKeys(); i++) + { + IBezFloatKey key; + ikc->GetKey(i, &key); +// fprintf(pStream, "%s\t\t\t%s %d\t%s", indent.data(), ID_BEZIER_FLOAT_KEY, key.time, Format(key.val)); +// fprintf(pStream, "\t%s\t%s\t%d\n", Format(key.intan), Format(key.outtan), key.flags); + } +// fprintf(pStream,"%s\t\t}\n", indent.data()); + } + else if (ikc && cont->ClassID() == Class_ID(LININTERP_FLOAT_CLASS_ID, 0)) + { +// fprintf(pStream,"%s\t\t%s {\n", indent.data(), ID_CONTROL_FLOAT_LINEAR); + for (i=0; iGetNumKeys(); i++) + { + ILinFloatKey key; + ikc->GetKey(i, &key); +// fprintf(pStream, "%s\t\t\t%s %d\t%s\n", indent.data(), ID_FLOAT_KEY, key.time, Format(key.val)); + } +// fprintf(pStream,"%s\t\t}\n", indent.data()); + } + else + { + // Unknown controller, no key interface or sample on demand - + // This might be a procedural controller or something else we + // don't know about. The last resort is to get the value from the + // controller at every n frames. + + TSTR name; + cont->GetClassName(name); +// fprintf(pStream,"%s\t\t%s \"%s\" {\n", indent.data(), ID_CONTROL_FLOAT_SAMPLE, FixupName(name)); + + // If it is animated at all... + if (cont->IsAnimated()) + { + // Get the range of the controller animation + Interval range; + // Get range of full animation + Interval animRange = ip->GetAnimRange(); + TimeValue t = cont->GetTimeRange(TIMERANGE_ALL).Start(); + float value; + + // While we are inside the animation... + while (animRange.InInterval(t)) + { + // Sample the controller + range = FOREVER; + cont->GetValue(t, &value, range); + + // Set time to start of controller validity interval + t = range.Start(); + + // Output the sample +// fprintf(pStream, "%s\t\t\t%s %d\t%s\n", indent.data(), ID_FLOAT_KEY, t, Format(value)); + + // If the end of the controller validity is beyond the + // range of the animation + if (range.End() > cont->GetTimeRange(TIMERANGE_ALL).End()) + { + break; + } + else + { + t = (range.End()/GetTicksPerFrame()+1) * GetTicksPerFrame(); + } + } + } +// fprintf(pStream,"%s\t\t}\n", indent.data()); + } +} + + +void AsciiExp::DumpPosKeys(Control* cont) +{ + if (!cont) return; + + int i; + IKeyControl *ikc = GetKeyControlInterface(cont); + + // TCB position + if (ikc && cont->ClassID() == Class_ID(TCBINTERP_POSITION_CLASS_ID, 0)) + { + int numKeys; + if (numKeys = ikc->GetNumKeys()) + { +// fprintf(pStream,"%s\t\t%s {\n", indent.data(), ID_CONTROL_POS_TCB); + for (i=0; iGetKey(i, &key); +// fprintf(pStream, "%s\t\t\t%s %d\t%s", indent.data(), ID_TCB_POS_KEY, key.time, Format(key.val)); +// fprintf(pStream, "\t%s\t%s\t%s\t%s\t%s\n", Format(key.tens), Format(key.cont), Format(key.bias), Format(key.easeIn), Format(key.easeOut)); + } +// fprintf(pStream,"%s\t\t}\n", indent.data()); + } + } + // Bezier position + else if (ikc && cont->ClassID() == Class_ID(HYBRIDINTERP_POSITION_CLASS_ID, 0)) + { + int numKeys; + if(numKeys = ikc->GetNumKeys()) + { +// fprintf(pStream,"%s\t\t%s {\n", indent.data(), ID_CONTROL_POS_BEZIER); + for (i=0; iGetKey(i, &key); +// fprintf(pStream, "%s\t\t\t%s %d\t%s", indent.data(), ID_BEZIER_POS_KEY, key.time, Format(key.val)); +// fprintf(pStream, "\t%s\t%s\t%d\n", Format(key.intan), Format(key.outtan), key.flags); + } +// fprintf(pStream,"%s\t\t}\n", indent.data()); + } + } + // Linear position + else if (ikc && cont->ClassID() == Class_ID(LININTERP_POSITION_CLASS_ID, 0)) + { + int numKeys; + if(numKeys = ikc->GetNumKeys()) + { +// fprintf(pStream,"%s\t\t%s {\n", indent.data(), ID_CONTROL_POS_LINEAR); + for (i=0; iGetKey(i, &key); +// fprintf(pStream, "%s\t\t\t%s %d\t%s\n", indent.data(), ID_POS_KEY, key.time, Format(key.val)); + } +// fprintf(pStream,"%s\t\t}\n", indent.data()); + } + } +} + + +void AsciiExp::DumpRotKeys(Control* cont) +{ + if (!cont) return; + + int i; + IKeyControl *ikc = GetKeyControlInterface(cont); + + if (ikc && cont->ClassID() == Class_ID(TCBINTERP_ROTATION_CLASS_ID, 0)) + { + int numKeys; + if (numKeys = ikc->GetNumKeys()) + { +// fprintf(pStream,"%s\t\t%s {\n", indent.data(), ID_CONTROL_ROT_TCB); + for (i=0; iGetKey(i, &key); +// fprintf(pStream, "%s\t\t\t%s %d\t%s", indent.data(), ID_TCB_ROT_KEY, key.time, Format(key.val)); +// fprintf(pStream, "\t%s\t%s\t%s\t%s\t%s\n", Format(key.tens), Format(key.cont), Format(key.bias), Format(key.easeIn), Format(key.easeOut)); + } +// fprintf(pStream,"%s\t\t}\n", indent.data()); + } + } + else if (ikc && cont->ClassID() == Class_ID(HYBRIDINTERP_ROTATION_CLASS_ID, 0)) + { + int numKeys; + if (numKeys = ikc->GetNumKeys()) + { +// fprintf(pStream,"%s\t\t%s {\n", indent.data(), ID_CONTROL_ROT_BEZIER); + for (i=0; iGetKey(i, &key); + // Quaternions are converted to AngAxis when written to file + // There is no intan/outtan for Quat Rotations +// fprintf(pStream, "%s\t\t\t%s %d\t%s\n", indent.data(), ID_ROT_KEY, key.time, Format(key.val)); + } +// fprintf(pStream,"%s\t\t}\n", indent.data()); + } + } + else if (ikc && cont->ClassID() == Class_ID(LININTERP_ROTATION_CLASS_ID, 0)) + { + int numKeys; + if (numKeys = ikc->GetNumKeys()) + { +// fprintf(pStream,"%s\t\t%s {\n", indent.data(), ID_CONTROL_ROT_LINEAR); + for (i=0; iGetKey(i, &key); + // Quaternions are converted to AngAxis when written to file +// fprintf(pStream, "%s\t\t\t%s %d\t%s\n", indent.data(), ID_ROT_KEY, key.time, Format(key.val)); + } +// fprintf(pStream,"%s\t\t}\n", indent.data()); + } + } +} + +void AsciiExp::DumpScaleKeys(Control* cont) +{ + if (!cont) return; + + int i; + IKeyControl *ikc = GetKeyControlInterface(cont); + + if (ikc && cont->ClassID() == Class_ID(TCBINTERP_SCALE_CLASS_ID, 0)) + { + int numKeys; + if (numKeys = ikc->GetNumKeys()) + { +// fprintf(pStream,"%s\t\t%s {\n", indent.data(), ID_CONTROL_SCALE_TCB); + for (i=0; iGetKey(i, &key); +// fprintf(pStream, "%s\t\t\t%s %d\t%s", indent.data(), ID_TCB_SCALE_KEY, key.time, Format(key.val)); +// fprintf(pStream, "\t%s\t%s\t%s\t%s\t%s\n", Format(key.tens), Format(key.cont), Format(key.bias), Format(key.easeIn), Format(key.easeOut)); + } +// fprintf(pStream,"%s\t\t}\n", indent.data()); + } + } + else if (ikc && cont->ClassID() == Class_ID(HYBRIDINTERP_SCALE_CLASS_ID, 0)) + { + int numKeys; + if (numKeys = ikc->GetNumKeys()) + { +// fprintf(pStream,"%s\t\t%s {\n", indent.data(), ID_CONTROL_SCALE_BEZIER); + for (i=0; iGetKey(i, &key); +// fprintf(pStream, "%s\t\t\t%s %d\t%s", indent.data(), ID_BEZIER_SCALE_KEY, key.time, Format(key.val)); +// fprintf(pStream, "\t%s\t%s\t%d\n", Format(key.intan), Format(key.outtan), key.flags); + } +// fprintf(pStream,"%s\t\t}\n", indent.data()); + } + } + else if (ikc && cont->ClassID() == Class_ID(LININTERP_SCALE_CLASS_ID, 0)) + { + int numKeys; + if (numKeys = ikc->GetNumKeys()) + { +// fprintf(pStream,"%s\t\t%s {\n", indent.data(), ID_CONTROL_SCALE_LINEAR); + for (i=0; iGetKey(i, &key); +// fprintf(pStream, "%s\t\t\t%s %d\t%s\n", indent.data(), ID_SCALE_KEY, key.time, Format(key.val)); + } +// fprintf(pStream,"%s\t\t}\n", indent.data()); + } + } +} + + +// Not truly the correct way to compare floats of arbitary magnitude... +BOOL EqualPoint3(Point3 p1, Point3 p2) +{ + if (fabs(p1.x - p2.x) > ALMOST_ZERO) return FALSE; + if (fabs(p1.y - p2.y) > ALMOST_ZERO) return FALSE; + if (fabs(p1.z - p2.z) > ALMOST_ZERO) return FALSE; + return TRUE; +} + + +// Determine if a TM controller is known by the system. +AsciiExp::IsKnownController(Control* cont) +{ + ulong partA, partB; + + if (!cont) return FALSE; + + partA = cont->ClassID().PartA(); + partB = cont->ClassID().PartB(); + + if (partB != 0x00) return FALSE; + + switch (partA) + { + case TCBINTERP_POSITION_CLASS_ID: + case TCBINTERP_ROTATION_CLASS_ID: + case TCBINTERP_SCALE_CLASS_ID: + case HYBRIDINTERP_POSITION_CLASS_ID: + case HYBRIDINTERP_ROTATION_CLASS_ID: + case HYBRIDINTERP_SCALE_CLASS_ID: + case LININTERP_POSITION_CLASS_ID: + case LININTERP_ROTATION_CLASS_ID: + case LININTERP_SCALE_CLASS_ID: + return TRUE; + } + + return FALSE; +} diff --git a/Utils/GinExp/ExpBone.cpp b/Utils/GinExp/ExpBone.cpp new file mode 100644 index 000000000..77490345d --- /dev/null +++ b/Utils/GinExp/ExpBone.cpp @@ -0,0 +1,991 @@ +/*========================================================================= + + EXPBONE.CPP + + Author: Tim Swann @ CLIMAX + Created: + Project: + Purpose: + + Copyright (c) 1998 Climax Development Ltd + +===========================================================================*/ + +/*---------------------------------------------------------------------- + Includes + -------- */ + +#include "AsciiExp.h" + + +/* Std Lib + ------- */ + +/* Glib + ---- */ + +/* Local + ----- */ + +/* Graphics + -------- */ + +/*---------------------------------------------------------------------- + Tyepdefs && Defines + ------------------- */ + +/*---------------------------------------------------------------------- + Structure defintions + -------------------- */ + +/*---------------------------------------------------------------------- + Positional Vars + --------------- */ + +/*---------------------------------------------------------------------- + Function Prototypes + ------------------- */ + +/*---------------------------------------------------------------------- + Vars + ---- */ + +/*---------------------------------------------------------------------- + Data + ---- */ + +/*---------------------------------------------------------------------- + Function: + Purpose: + Params: + Returns: + ---------------------------------------------------------------------- */ + + +void AsciiExp::ExportBones( INode* node ) +{ + + nbBones = 0; + + WriteChunkHdr( (char*)BONE_ID, 0); + fwrite( &nCurObj, sizeof( Uint32 ), 1, expStream ); + + ExportLimb( node ); +} + + +/*---------------------------------------------------------------------- + Function: + Purpose: + Params: + Returns: + ---------------------------------------------------------------------- */ + +static bool ThisIsABone( INode * node ) +{ + bool retval = false; + char * nodeName; + + nodeName = node->GetName(); + +// if (nodeName[0] == 'R' && +// nodeName[1] == 'O' && +// nodeName[2] == 'O' && +// nodeName[3] == 'T') retval = true; + + retval = true; + + return (retval); +} + + +/*---------------------------------------------------------------------- + Function: + Purpose: + Params: + Returns: + ---------------------------------------------------------------------- */ + +int AsciiExp::ValidateBoneKids( INode * node ) +{ + int nbChilds = node->NumberOfChildren(); + int childCnt = 0; + + return nbChilds; +} + + +bool IsParent(INode *p, INode *c) +{ + while (!c->IsRootNode()) { + c=c->GetParentNode(); + if (c==p) + return true; + } + return false; +} +/*---------------------------------------------------------------------- + Function: + Purpose: + Params: + Returns: + ---------------------------------------------------------------------- */ + +void AsciiExp::ExportLimb( INode * node ) +{ + INode * ChildNode; + ObjectState os = node->EvalWorldState( ip->GetAnimRange().Start() ); + Object * obj = node->EvalWorldState(ip->GetAnimRange().Start()).obj; + long nbChilds; + Matrix3 tm; + AffineParts ap; + + Quat q; + char name[256]; + Point3 fpos; + Point3 pos, p2; + + // EXPORT BONE + sprintf( name, "%s", node->GetName() ); + fprintf( tempStream, "OUTPUTTING - %s\n", name ); // DEBUG FILE + fwrite( &name, sizeof(char), MAX_NAME_LENGTH, expStream ); // WRITE BONE NAME + + // EXPORT WEIGHTS + long nbVerts = 0; + if (nbWeights) + { + fprintf( tempStream, "WEIGHTS:\n" ); + + for (int i=0;iGetName(), Weights[i].names[n])) nbVerts++; + } + } + fprintf( tempStream, " nbVerts = %d\n", nbVerts ); + fwrite( &nbVerts, sizeof(long), 1, expStream ); // WRITE BONE WEIGHT COUNT + + if (nbVerts) + { + for (int i=0;iGetName(), Weights[i].names[n])) + { + fwrite( &Weights[i].vertno, sizeof(long), 1, expStream ); + fwrite( &Weights[i].weights[n], sizeof(float), 1, expStream ); + fwrite( &Weights[i].Offsets[n].x, sizeof(float), 1, expStream); + fwrite( &Weights[i].Offsets[n].z, sizeof(float), 1, expStream); + fwrite( &Weights[i].Offsets[n].y, sizeof(float), 1, expStream); + } + } + } + } + } else { + fwrite( &nbVerts, sizeof(long), 1, expStream ); // WRITE BONE WEIGHT COUNT + } + + // EXPORT CHILDREN + + nbBones++; + nbChilds = node->NumberOfChildren(); + fwrite( &nbChilds, sizeof(int), 1, expStream); + for (int c=0;cGetChildNode(c); + if (ChildNode) ExportLimb( ChildNode ); + } +} + + +/*---------------------------------------------------------------------- + Function: + Purpose: + Params: + Returns: + ---------------------------------------------------------------------- */ + +void AsciiExp::ExportBoneAnim( INode* node ) +{ + TimeValue start = ip->GetAnimRange().Start(); + TimeValue end = ip->GetAnimRange().End(); + TimeValue t; + int delta = GetTicksPerFrame(); + Matrix3 tm; + AffineParts ap; + Point3 prevPos; + Point3 startScale; + Quat prevQ; + Quat scaleQ; + Quat q; + Quat InvQ; + INode * ChildNode; + ObjectState os = node->EvalWorldState( ip->GetAnimRange().Start() ); + Object * obj = node->EvalWorldState(ip->GetAnimRange().Start()).obj; + long nbChilds; + + + for (t=start; t<=end; t+=delta) + { + // TRANSLATION + if (0)//node->IsRootNode()) + { + tm = node->GetNodeTM( t ); + } else + { + tm = node->GetNodeTM(t) * Inverse(node->GetParentTM(t)); + } + decomp_affine(tm, &ap); + + fwrite( &ap.t.x, sizeof(float), 1, expStream ); // WRITE BONE X-POS + fwrite( &ap.t.z, sizeof(float), 1, expStream ); // WRITE BONE Y-POS + fwrite( &ap.t.y, sizeof(float), 1, expStream ); // WRITE BONE Z-POS + + fwrite( &ap.q.x, sizeof(float), 1, expStream ); // WRITE BONE ROT X-AXIS + fwrite( &ap.q.z, sizeof(float), 1, expStream ); // WRITE BONE ROT Y-AXIS + fwrite( &ap.q.y, sizeof(float), 1, expStream ); // WRITE BONE ROT Z-AXIS + fwrite( &ap.q.w, sizeof(float), 1, expStream ); // WRITE BONE ROT W ANGLE + + fwrite( &ap.k.x, sizeof(float), 1, expStream ); // WRITE BONE X-POS + fwrite( &ap.k.z, sizeof(float), 1, expStream ); // WRITE BONE Y-POS + fwrite( &ap.k.y, sizeof(float), 1, expStream ); // WRITE BONE Z-POS + + fwrite( &ap.u.x, sizeof(float), 1, expStream ); // WRITE BONE ROT X-AXIS + fwrite( &ap.u.z, sizeof(float), 1, expStream ); // WRITE BONE ROT Y-AXIS + fwrite( &ap.u.y, sizeof(float), 1, expStream ); // WRITE BONE ROT Z-AXIS + fwrite( &ap.u.w, sizeof(float), 1, expStream ); // WRITE BONE ROT W ANGLE + } + + nbChilds = node->NumberOfChildren(); + for (int c=0;cGetChildNode(c); + if (ChildNode) ExportBoneAnim( ChildNode ); + } +} + +void AsciiExp::ExportBoneKeyAnim( INode* node) +{ + TimeValue start = ip->GetAnimRange().Start(); + TimeValue end = ip->GetAnimRange().End(); + int delta = GetTicksPerFrame(); + Matrix3 tm; + AffineParts ap, StartAp; + Point3 prevPos; + Point3 startScale; + Quat prevQ; + Quat scaleQ; + Quat q; + Quat InvQ; + INode * ChildNode; + ObjectState os = node->EvalWorldState( ip->GetAnimRange().Start() ); + Object * obj = node->EvalWorldState(ip->GetAnimRange().Start()).obj; + long nbChilds; + + int num; + int i; + int FrameN; + Interval ivalid; + IKey k; + Control *c; + IKeyControl *ikeys; + ITCBPoint3Key tcbPosKey; + ITCBRotKey tcbRotKey; + ITCBScaleKey tcbSclKey; + IBezPoint3Key bezPosKey; + IBezQuatKey bezRotKey; + IBezScaleKey bezSclKey; + ILinPoint3Key linPosKey; + ILinRotKey linRotKey; + ILinScaleKey linSclKey; + TimeValue time; + + + if (node->EvalWorldState(ip->GetAnimRange().Start()).obj) { + + TimeList tl; + tl.Clear(); + INode *KeyNode = node; + c = KeyNode->GetTMController(); + if (c) { + c = c->GetPositionController(); + if (c) { + ikeys = GetKeyControlInterface(c); + if (ikeys) { + + num = ikeys->GetNumKeys(); + if (num >0) + { + for (i = 0; iClassID() == Class_ID(TCBINTERP_POSITION_CLASS_ID, 0)) { + ikeys->GetKey(i, &tcbPosKey); + time = tcbPosKey.time; + } else + if (c->ClassID() == Class_ID(HYBRIDINTERP_POSITION_CLASS_ID, 0)) { + ikeys->GetKey(i, &bezPosKey); + time = bezPosKey.time; + } else + if (c->ClassID() == Class_ID(LININTERP_POSITION_CLASS_ID, 0)) { + ikeys->GetKey(i, &linPosKey); + time = linPosKey.time; + } + tl.Add(time); + } + } + } + } + } + c = KeyNode->GetTMController(); + if (c) { + c = c->GetRotationController(); + if (c) { + ikeys = GetKeyControlInterface(c); + if (ikeys) { + + num = ikeys->GetNumKeys(); + if (num >0) + { + + for (i = 0; iClassID() == Class_ID(TCBINTERP_ROTATION_CLASS_ID, 0)) { + ikeys->GetKey(i, &tcbRotKey); + int rots; + if (i) + { + ITCBRotKey oldtcbRotKey; + ikeys->GetKey(i-1, &oldtcbRotKey); + rots = fabs(tcbRotKey.val.angle - oldtcbRotKey.val.angle) / (0.6667 * PI) + 1; + for (int k=1; kClassID() == Class_ID(HYBRIDINTERP_ROTATION_CLASS_ID, 0)) { + ikeys->GetKey(i, &bezRotKey); + time = bezRotKey.time; + } else + if (c->ClassID() == Class_ID(LININTERP_ROTATION_CLASS_ID, 0)) { + ikeys->GetKey(i, &linRotKey); + time = linRotKey.time; + } + tl.Add(time); + } + } + } + } + } + c = KeyNode->GetTMController(); + if (c) { + c = c->GetScaleController(); + if (c) { + ikeys = GetKeyControlInterface(c); + if (ikeys) { + + num = ikeys->GetNumKeys(); + if (num >0) + { + for (i = 0; iClassID() == Class_ID(TCBINTERP_SCALE_CLASS_ID, 0)) { + ikeys->GetKey(i, &tcbSclKey); + time = tcbSclKey.time; + } else + if (c->ClassID() == Class_ID(HYBRIDINTERP_SCALE_CLASS_ID, 0)) { + ikeys->GetKey(i, &bezSclKey); + time = bezSclKey.time; + } else + if (c->ClassID() == Class_ID(LININTERP_SCALE_CLASS_ID, 0)) { + ikeys->GetKey(i, &linSclKey); + time = linSclKey.time; + } + + tl.Add(time); + } + } + } + } + } + if (tl.Count()) { + num = tl.Count(); + WriteChunkHdr( (char*)KEY_ID, 0); + fwrite( &CHANNEL_TYPE_BONE, sizeof( Uint32 ), 1, expStream ); + fwrite( &CHANNEL_INFO_ALL, sizeof( Uint32) , 1, expStream); + fwrite( &nCurObj, sizeof( Uint32 ), 1, expStream ); + fwrite( &CurBone, sizeof( Uint32 ), 1, expStream ); + fwrite( &num, sizeof( Uint32 ), 1, expStream ); + + for (num = 0; num GetNodeTM(time) * Inverse(node->GetParentTM(time)); + + FrameN = (int)((float)(time - start) / delta); + + decomp_affine(tm, &ap); + if (num == 0) + StartAp = ap; + + fwrite( &FrameN, sizeof(Uint32), 1, expStream); + fwrite( &ap.t.x, sizeof(float), 1, expStream ); // WRITE BONE X-POS + fwrite( &ap.t.z, sizeof(float), 1, expStream ); // WRITE BONE Y-POS + fwrite( &ap.t.y, sizeof(float), 1, expStream ); // WRITE BONE Z-POS + + fwrite( &ap.q.x, sizeof(float), 1, expStream ); // WRITE BONE X-POS + fwrite( &ap.q.z, sizeof(float), 1, expStream ); // WRITE BONE Y-POS + fwrite( &ap.q.y, sizeof(float), 1, expStream ); // WRITE BONE Z-POS + fwrite( &ap.q.w, sizeof(float), 1, expStream ); // WRITE BONE Z-POS + + fwrite( &ap.k.x, sizeof(float), 1, expStream ); // WRITE BONE X-POS + fwrite( &ap.k.z, sizeof(float), 1, expStream ); // WRITE BONE Y-POS + fwrite( &ap.k.y, sizeof(float), 1, expStream ); // WRITE BONE Z-POS + + fwrite( &ap.u.x, sizeof(float), 1, expStream ); // WRITE BONE X-POS + fwrite( &ap.u.z, sizeof(float), 1, expStream ); // WRITE BONE Y-POS + fwrite( &ap.u.y, sizeof(float), 1, expStream ); // WRITE BONE Z-POS + fwrite( &ap.u.w, sizeof(float), 1, expStream ); // WRITE BONE Z-POS + } + } + + } + CurBone++; + nbChilds = node->NumberOfChildren(); + for (int cn=0;cnGetChildNode(cn); + if (ChildNode) ExportBoneKeyAnim( ChildNode); + } +} + + +void AsciiExp::ExportBoneKeyAnimPos( INode* node) +{ + TimeValue start = ip->GetAnimRange().Start(); + TimeValue end = ip->GetAnimRange().End(); + int delta = GetTicksPerFrame(); + Matrix3 tm; + AffineParts ap; + Point3 prevPos; + Point3 startScale; + Quat prevQ; + Quat scaleQ; + Quat q; + Quat InvQ; + INode * ChildNode; + ObjectState os = node->EvalWorldState( ip->GetAnimRange().Start() ); + Object * obj = node->EvalWorldState(ip->GetAnimRange().Start()).obj; + long nbChilds; + + int num; + int i; + int FrameN; + Interval ivalid; + IKey k; + Control *c; + IKeyControl *ikeys; + ITCBPoint3Key tcbPosKey; + ITCBRotKey tcbRotKey; + ITCBScaleKey tcbScaleKey; + IBezPoint3Key bezPosKey; + IBezQuatKey bezRotKey; + IBezScaleKey bezScaleKey; + ILinPoint3Key linPosKey; + ILinRotKey linRotKey; + ILinScaleKey linScaleKey; + TimeValue time; + + if (node->EvalWorldState(ip->GetAnimRange().Start()).obj) { + TimeList tl; + tl.Clear(); + INode *KeyNode = node; + while (KeyNode) { + c = KeyNode->GetTMController(); + if (c) { + c = c->GetPositionController(); + if (c) { + ikeys = GetKeyControlInterface(c); + if (ikeys) { + + num = ikeys->GetNumKeys(); + if (num >0) + { + for (i = 0; iClassID() == Class_ID(TCBINTERP_POSITION_CLASS_ID, 0)) { + ikeys->GetKey(i, &tcbPosKey); + time = tcbPosKey.time; + } else + if (c->ClassID() == Class_ID(HYBRIDINTERP_POSITION_CLASS_ID, 0)) { + ikeys->GetKey(i, &bezPosKey); + time = bezPosKey.time; + } else + if (c->ClassID() == Class_ID(LININTERP_POSITION_CLASS_ID, 0)) { + ikeys->GetKey(i, &linPosKey); + time = linPosKey.time; + } + tl.Add(time); + } + } + } + } + } + if (KeyNode->IsRootNode()) + break; + KeyNode = KeyNode->GetParentNode(); + } + if (tl.Count()) { + num = tl.Count(); + WriteChunkHdr( (char*)KEY_ID, 0); + fwrite( &CHANNEL_TYPE_BONE, sizeof( Uint32 ), 1, expStream ); + fwrite( &CHANNEL_INFO_POSITION, sizeof( Uint32) , 1, expStream); + fwrite( &nCurObj, sizeof( Uint32 ), 1, expStream ); + fwrite( &CurBone, sizeof( Uint32 ), 1, expStream ); + fwrite( &num, sizeof( Uint32 ), 1, expStream ); + + for (num = 0; num GetNodeTM(time) * Inverse(node->GetParentTM(time)); + + FrameN = (int)((float)(time - start) / delta); + + decomp_affine(tm, &ap); + + fwrite( &FrameN, sizeof(Uint32), 1, expStream); + fwrite( &ap.t.x, sizeof(float), 1, expStream ); // WRITE BONE X-POS + fwrite( &ap.t.z, sizeof(float), 1, expStream ); // WRITE BONE Y-POS + fwrite( &ap.t.y, sizeof(float), 1, expStream ); // WRITE BONE Z-POS + } + } + + } + CurBone++; + nbChilds = node->NumberOfChildren(); + for (int cn=0;cnGetChildNode(cn); + if (ChildNode) ExportBoneKeyAnimPos( ChildNode); + } +} + +void AsciiExp::ExportBoneKeyAnimRot( INode* node) +{ + TimeValue start = ip->GetAnimRange().Start(); + TimeValue end = ip->GetAnimRange().End(); + int delta = GetTicksPerFrame(); + Matrix3 tm; + AffineParts ap; + Point3 prevPos; + Point3 startScale; + Quat prevQ; + Quat scaleQ; + Quat q; + Quat InvQ; + INode * ChildNode; + ObjectState os = node->EvalWorldState( ip->GetAnimRange().Start() ); + Object * obj = node->EvalWorldState(ip->GetAnimRange().Start()).obj; + long nbChilds; + + + int num; + int i; + int FrameN; + Interval ivalid; + IKey k; + Control *c; + IKeyControl *ikeys; + ITCBPoint3Key tcbPosKey; + ITCBRotKey tcbRotKey; + ITCBScaleKey tcbScaleKey; + IBezPoint3Key bezPosKey; + IBezQuatKey bezRotKey; + IBezScaleKey bezScaleKey; + ILinPoint3Key linPosKey; + ILinRotKey linRotKey; + ILinScaleKey linScaleKey; + TimeValue time; + + if (node->EvalWorldState(ip->GetAnimRange().Start()).obj) { + TimeList tl; + tl.Clear(); + INode *KeyNode = node; + while (KeyNode) { + c = KeyNode->GetTMController(); + if (c) { + c = c->GetRotationController(); + if (c) { + ikeys = GetKeyControlInterface(c); + if (ikeys) { + + num = ikeys->GetNumKeys(); + if (num >0) + { + + for (i = 0; iClassID() == Class_ID(TCBINTERP_ROTATION_CLASS_ID, 0)) { + ikeys->GetKey(i, &tcbRotKey); + time = tcbRotKey.time; + } else + if (c->ClassID() == Class_ID(HYBRIDINTERP_ROTATION_CLASS_ID, 0)) { + ikeys->GetKey(i, &bezRotKey); + time = bezRotKey.time; + } else + if (c->ClassID() == Class_ID(LININTERP_ROTATION_CLASS_ID, 0)) { + ikeys->GetKey(i, &linRotKey); + time = linRotKey.time; + } + tl.Add(time); + } + } + } + } + } + if (KeyNode->IsRootNode()) + break; + KeyNode = KeyNode->GetParentNode(); + } + + if (tl.Count()) { + num = tl.Count(); + WriteChunkHdr( (char*)KEY_ID, 0); + fwrite( &CHANNEL_TYPE_BONE, sizeof( Uint32 ), 1, expStream ); + fwrite( &CHANNEL_INFO_ROTATION, sizeof( Uint32) , 1, expStream); + fwrite( &nCurObj, sizeof( Uint32 ), 1, expStream ); + fwrite( &CurBone, sizeof( Uint32 ), 1, expStream ); + fwrite( &num, sizeof( Uint32 ), 1, expStream ); + for (num = 0; num GetNodeTM(time) * Inverse(node->GetParentTM(time)); + + FrameN = (int)((float)(time - start) / delta); + + decomp_affine(tm, &ap); + + fwrite( &FrameN, sizeof(Uint32), 1, expStream); + fwrite( &ap.q.x, sizeof(float), 1, expStream ); // WRITE BONE X-POS + fwrite( &ap.q.z, sizeof(float), 1, expStream ); // WRITE BONE Y-POS + fwrite( &ap.q.y, sizeof(float), 1, expStream ); // WRITE BONE Z-POS + fwrite( &ap.q.w, sizeof(float), 1, expStream ); // WRITE BONE Z-POS + } + } + } + + CurBone++; + + nbChilds = node->NumberOfChildren(); + for (int cn=0;cnGetChildNode(cn); + if (ChildNode) ExportBoneKeyAnimRot( ChildNode); + } +} + +void AsciiExp::ExportBoneKeyAnimScl( INode* node) +{ + TimeValue start = ip->GetAnimRange().Start(); + TimeValue end = ip->GetAnimRange().End(); + int delta = GetTicksPerFrame(); + Matrix3 tm; + AffineParts ap; + Point3 prevPos; + Point3 startScale; + Quat prevQ; + Quat scaleQ; + Quat q; + Quat InvQ; + INode * ChildNode; + ObjectState os = node->EvalWorldState( ip->GetAnimRange().Start() ); + Object * obj = node->EvalWorldState(ip->GetAnimRange().Start()).obj; + long nbChilds; + + int num; + int i; + int FrameN; + Interval ivalid; + IKey k; + Control *c; + IKeyControl *ikeys; + ITCBPoint3Key tcbPosKey; + ITCBRotKey tcbRotKey; + ITCBScaleKey tcbSclKey; + IBezPoint3Key bezPosKey; + IBezQuatKey bezRotKey; + IBezScaleKey bezSclKey; + ILinPoint3Key linPosKey; + ILinRotKey linRotKey; + ILinScaleKey linSclKey; + TimeValue time; + + char text[200]; + sprintf(text, "%s", node->GetName()); + + if (node->EvalWorldState(ip->GetAnimRange().Start()).obj) { + TimeList tl; + tl.Clear(); + INode *KeyNode = node; + while (KeyNode) { + c = KeyNode->GetTMController(); + if (c) { + c = c->GetScaleController(); + if (c) { + ikeys = GetKeyControlInterface(c); + if (ikeys) { + + num = ikeys->GetNumKeys(); + if (num >0) + { + for (i = 0; iClassID() == Class_ID(TCBINTERP_SCALE_CLASS_ID, 0)) { + ikeys->GetKey(i, &tcbSclKey); + time = tcbSclKey.time; + } else + if (c->ClassID() == Class_ID(HYBRIDINTERP_SCALE_CLASS_ID, 0)) { + ikeys->GetKey(i, &bezSclKey); + time = bezSclKey.time; + } else + if (c->ClassID() == Class_ID(LININTERP_SCALE_CLASS_ID, 0)) { + ikeys->GetKey(i, &linSclKey); + time = linSclKey.time; + } + + tl.Add(time); + } + } + } + } + } + if (KeyNode->IsRootNode()) + break; + KeyNode = KeyNode->GetParentNode(); + } + + if (tl.Count()) { + num = tl.Count(); + WriteChunkHdr( (char*)KEY_ID, 0); + fwrite( &CHANNEL_TYPE_BONE, sizeof( Uint32 ), 1, expStream ); + fwrite( &CHANNEL_INFO_SCALE, sizeof( Uint32) , 1, expStream); + fwrite( &nCurObj, sizeof( Uint32 ), 1, expStream ); + fwrite( &CurBone, sizeof( Uint32 ), 1, expStream ); + fwrite( &num, sizeof( Uint32 ), 1, expStream ); + + for (num = 0; num GetNodeTM(time) * Inverse(node->GetParentTM(time)); + + FrameN = (int)((float)(time - start) / delta); + + decomp_affine(tm, &ap); + + fwrite( &FrameN, sizeof(Uint32), 1, expStream); + fwrite( &ap.k.x, sizeof(float), 1, expStream ); // WRITE BONE X-POS + fwrite( &ap.k.z, sizeof(float), 1, expStream ); // WRITE BONE Y-POS + fwrite( &ap.k.y, sizeof(float), 1, expStream ); // WRITE BONE Z-POS + + fwrite( &ap.u.x, sizeof(float), 1, expStream ); // WRITE BONE X-POS + fwrite( &ap.u.z, sizeof(float), 1, expStream ); // WRITE BONE Y-POS + fwrite( &ap.u.y, sizeof(float), 1, expStream ); // WRITE BONE Z-POS + fwrite( &ap.u.w, sizeof(float), 1, expStream ); // WRITE BONE Z-POS + } + } + } + CurBone++; + nbChilds = node->NumberOfChildren(); + for (int cn=0;cnGetChildNode(cn); + if (ChildNode) ExportBoneKeyAnimScl( ChildNode); + } +} + +/*---------------------------------------------------------------------- + Function: + Purpose: + Params: + Returns: + ---------------------------------------------------------------------- */ + +void AsciiExp::ExportWeights( INode* node ) +{ + Object * objPtr = NULL; + Modifier * phyMod = NULL; + IPhysiqueExport * phyExport = NULL; + IPhyContextExport * contextExport = NULL; + IPhyVertexExport * vtxExport = NULL; + IPhyRigidVertex * rvtExport = NULL; + IPhyBlendedRigidVertex * brvExport = NULL; + Point3 offsetvector; + INode * bonenode = NULL; + float weight = 0.f; + int nbNodes = 0; + int vtype = 0; + INode * RootNode = NULL; + ModContext *mc; + + + Weights = NULL; + + phyMod = FindPhysiqueModifier (node, &mc); + if (phyMod) + { + PhyMc = mc; + phyExport = (IPhysiqueExport *)phyMod->GetInterface( I_PHYINTERFACE ); + if (phyExport) + { + contextExport = (IPhyContextExport *)phyExport->GetContextInterface( node ); + if (contextExport) + { + contextExport->ConvertToRigid( TRUE ); + contextExport->AllowBlending( TRUE ); + + nbWeights = contextExport->GetNumberVertices(); + + Weights = (WEIGHTS *)malloc( nbWeights * sizeof(WEIGHTS) ); + + for (int i=0;iGetVertexInterface( i ); + + if (vtxExport) + { + vtype = vtxExport->GetVertexType(); + + if (vtype == RIGID_TYPE) + { + rvtExport = (IPhyRigidVertex *)vtxExport; + bonenode = rvtExport->GetNode(); + + Weights[i].vertno = i; + Weights[i].nodecount = 1; + Weights[i].weights[0] = 1.f; + Weights[i].Offsets[0] = rvtExport->GetOffsetVector(); + sprintf( Weights[i].names[0], "%s", bonenode->GetName() ); + } + else + { + brvExport = (IPhyBlendedRigidVertex *)vtxExport; + nbNodes = brvExport->GetNumberNodes(); + + Weights[i].vertno = i; + Weights[i].nodecount = nbNodes; + if (nbNodes > MAX_BONE_NODES) + { + MessageBox( MAX_hWnd, "INCREASE MAX_BONE_NODES", "ERROR", MB_OK ); + return; + } + for (int n=0;nGetNode( n ); + + weight = brvExport->GetWeight( n ); + offsetvector = brvExport->GetOffsetVector( n ); + + Weights[i].weights[n] = weight; + Weights[i].Offsets[n] = brvExport->GetOffsetVector(n); + sprintf( Weights[i].names[n], "%s", bonenode->GetName() ); + } + } + + contextExport->ReleaseVertexInterface( vtxExport ); + } + } + } + } + ExportBones(ip->GetRootNode()); + if (Weights) { + free (Weights); + Weights = NULL; + } + } +} + +bool AsciiExp::IsBoned(INode *node) +{ + if (FindPhysiqueModifier(node)) + return true; + return false; +} + +/*---------------------------------------------------------------------- + Function: + Purpose: + Params: + Returns: + ---------------------------------------------------------------------- */ + + +Modifier * AsciiExp::FindPhysiqueModifier (INode* node, ModContext**mc) +{ + // Get object from node. Abort if no object. + Object* ObjectPtr = node->GetObjectRef(); + + if (!ObjectPtr) return NULL; + + // Is derived object ? + if (ObjectPtr->SuperClassID() == GEN_DERIVOB_CLASS_ID) + { + // Yes -> Cast. + IDerivedObject* DerivedObjectPtr = static_cast(ObjectPtr); + + // Iterate over all entries of the modifier stack. + int ModStackIndex = 0; + while (ModStackIndex < DerivedObjectPtr->NumModifiers()) + { + // Get current modifier. + Modifier* ModifierPtr = DerivedObjectPtr->GetModifier(ModStackIndex); + + // Is this Physique ? + if (ModifierPtr->ClassID() == Class_ID(PHYSIQUE_CLASS_ID_A, PHYSIQUE_CLASS_ID_B)) + { + *mc = DerivedObjectPtr->GetModContext(ModStackIndex); + // Yes -> Exit. + return ModifierPtr; + } + + // Next modifier stack entry. + ModStackIndex++; + } + } + + // Not found. + return NULL; +} + +Modifier * AsciiExp::FindPhysiqueModifier (INode* node) +{ + // Get object from node. Abort if no object. + Object* ObjectPtr = node->GetObjectRef(); + + if (!ObjectPtr) return NULL; + + // Is derived object ? + if (ObjectPtr->SuperClassID() == GEN_DERIVOB_CLASS_ID) + { + // Yes -> Cast. + IDerivedObject* DerivedObjectPtr = static_cast(ObjectPtr); + + // Iterate over all entries of the modifier stack. + int ModStackIndex = 0; + while (ModStackIndex < DerivedObjectPtr->NumModifiers()) + { + // Get current modifier. + Modifier* ModifierPtr = DerivedObjectPtr->GetModifier(ModStackIndex); + + // Is this Physique ? + if (ModifierPtr->ClassID() == Class_ID(PHYSIQUE_CLASS_ID_A, PHYSIQUE_CLASS_ID_B)) + { + // Yes -> Exit. + return ModifierPtr; + } + + // Next modifier stack entry. + ModStackIndex++; + } + } + + // Not found. + return NULL; +} + + + +/*=========================================================================== + end */ diff --git a/Utils/GinExp/ExpCamLight.cpp b/Utils/GinExp/ExpCamLight.cpp new file mode 100644 index 000000000..a9c131a21 --- /dev/null +++ b/Utils/GinExp/ExpCamLight.cpp @@ -0,0 +1,237 @@ +/*========================================================================= + + .CPP + + Author: Tim Swann @ CLIMAX + Created: + Project: + Purpose: + + Copyright (c) 1998 Climax Development Ltd + +===========================================================================*/ + +/*---------------------------------------------------------------------- + Includes + -------- */ + +#include "AsciiExp.h" + + +/* Std Lib + ------- */ + +/* Glib + ---- */ + +/* Local + ----- */ + +/* Graphics + -------- */ + +/*---------------------------------------------------------------------- + Tyepdefs && Defines + ------------------- */ + +/*---------------------------------------------------------------------- + Structure defintions + -------------------- */ + +/*---------------------------------------------------------------------- + Positional Vars + --------------- */ + +/*---------------------------------------------------------------------- + Function Prototypes + ------------------- */ + +/*---------------------------------------------------------------------- + Vars + ---- */ + +/*---------------------------------------------------------------------- + Data + ---- */ + +/*---------------------------------------------------------------------- + Function: + Purpose: + Params: + Returns: + ---------------------------------------------------------------------- */ + +void AsciiExp::ExportCamera(INode* node) +{ + INode * target = node->GetTarget(); + CameraState cs; + TimeValue t = 0; + Interval valid = FOREVER; + Interval animRange = ip->GetAnimRange(); + ObjectState os = node->EvalWorldState(t); + CameraObject * cam = (CameraObject *)os.obj; + + + cam->EvalCameraState(t, valid, &cs); + + if (target) + { + WriteChunkHdr( (char*)CAMERA_ID, 0 ); + fwrite( &totFrames, sizeof( Uint32 ), 1, expStream ); + + DumpCamPosSample( node ); + DumpCamPosSample( target ); + } +} + + +/*---------------------------------------------------------------------- + ---------------------------------------------------------------------- */ + +void AsciiExp::DumpCamPosSample(INode* node) +{ + TimeValue start = ip->GetAnimRange().Start(); + TimeValue end = ip->GetAnimRange().End(); + TimeValue t; + int delta = GetTicksPerFrame() * 1; + Matrix3 tm; + AffineParts ap; + Point3 pos; + Point3 opos; + + + for (t=start; t<=end; t+=delta) + { + tm = node->GetNodeTM(t); + + decomp_affine(tm, &ap); + + pos = ap.t; + + opos.x = pos.x; + opos.y = pos.z; + opos.z = pos.y; + + fwrite( &opos.x, sizeof( float ), 1, expStream ); // WRITE CAMERA POSITION + fwrite( &opos.y, sizeof( float ), 1, expStream ); + fwrite( &opos.z, sizeof( float ), 1, expStream ); + } +} + + +/*---------------------------------------------------------------------- + Function: + Purpose: + Params: + Returns: + ---------------------------------------------------------------------- */ + +void AsciiExp::ExportLight(INode* node) +{ + Interval valid = FOREVER; + Matrix3 mtx = node->GetNodeTM( 0 ); + ObjectState os = node->EvalWorldState( 0 ); + GenLight * light; + LightState ls; + AffineParts ap; + Point3 pos; + Point3 opos; + float ang[4]; + Point3 oang; + Quat rot; + INode * target; + float hotsize; + float fallsize; + float attenStart; + float attenEnd; + + + + if (!os.obj) return; + + // EXPORT LIGHT + WriteChunkHdr( (char*)LIGHT_ID, 0 ); + + light = (GenLight*)os.obj; + light->EvalLightState( 0, valid, &ls ); + fwrite( &ls.type, sizeof(long), 1, expStream ); + + decomp_affine( mtx, &ap ); + + pos = ap.t; + + opos.x = pos.x; + opos.y = pos.z; + opos.z = pos.y; + + fwrite( &opos.x, sizeof( float ), 1, expStream ); // WRITE LIGHT POSITION + fwrite( &opos.y, sizeof( float ), 1, expStream ); + fwrite( &opos.z, sizeof( float ), 1, expStream ); + + rot.x = ap.q.x; + rot.y = ap.q.z; + rot.z = ap.q.y; + rot.w = ap.q.w; + QuatToEuler( rot, ang); + + oang.x = ang[0]; + oang.y = ang[1]; + oang.z = ang[2]; +// fwrite( &oang.x, sizeof( float ), 1, expStream ); // WRITE LIGHT ROTATION +// fwrite( &oang.y, sizeof( float ), 1, expStream ); +// fwrite( &oang.z, sizeof( float ), 1, expStream ); + + target = node->GetTarget(); + if (target) + { + mtx = target->GetNodeTM( 0 ); + decomp_affine( mtx, &ap ); + + pos = ap.t; + + opos.x = pos.x; + opos.y = pos.z; + opos.z = pos.y; + } + else + { +// opos.x = 0.f; +// opos.y = 0.f; +// opos.z = 0.f; + } + fwrite( &opos.x, sizeof( float ), 1, expStream ); // WRITE LIGHT POSITION + fwrite( &opos.y, sizeof( float ), 1, expStream ); + fwrite( &opos.z, sizeof( float ), 1, expStream ); + + fwrite( &ls.color.r, sizeof( float ), 1, expStream ); + fwrite( &ls.color.g, sizeof( float ), 1, expStream ); + fwrite( &ls.color.b, sizeof( float ), 1, expStream ); + fwrite( &ls.intens, sizeof( float ), 1, expStream ); + + + + hotsize = 0.f; + fallsize = 0.f; + attenStart = 0.f; + attenEnd = 0.f; + + if (ls.type != OMNI_LIGHT) + { + hotsize = ls.hotsize; + fallsize = ls.fallsize; + } + if (ls.type != DIR_LIGHT && ls.useAtten) + { + attenStart = ls.attenStart; + attenEnd = ls.attenEnd; + } + + fwrite( &hotsize, sizeof( float ), 1, expStream ); + fwrite( &fallsize, sizeof( float ), 1, expStream ); + fwrite( &attenStart, sizeof( float ), 1, expStream ); + fwrite( &attenEnd, sizeof( float ), 1, expStream ); +} + + +/*=========================================================================== + end */ diff --git a/Utils/GinExp/ExpFFD.cpp b/Utils/GinExp/ExpFFD.cpp new file mode 100644 index 000000000..5ee24ee29 --- /dev/null +++ b/Utils/GinExp/ExpFFD.cpp @@ -0,0 +1,550 @@ +#include "max.h" +#include "asciiexp.h" + +#define EDITMESH_CLASS_ID 0x00050 + +typedef Tab Point3Tab; +//typedef Tab AnimPoint3Tab; +typedef Tab FaceTab; + +class XFormProc { + public: + virtual Point3 proc(Point3& p, Matrix3 &mat, Matrix3 &imat)=0; + virtual void SetMat( Matrix3& mat ) {} + }; + +class MoveXForm : public XFormProc { + private: + Point3 delta, tdelta; + public: + Point3 proc(Point3& p, Matrix3 &mat, Matrix3 &imat) + { return p + tdelta; } + void SetMat( Matrix3& mat ) + { tdelta = VectorTransform(Inverse(mat),delta); } + MoveXForm(Point3 d) { delta = d; } + }; + +class RotateXForm : public XFormProc { + private: + Matrix3 rot, trot; + public: + Point3 proc(Point3& p, Matrix3 &mat, Matrix3 &imat) + { return (trot*p)*imat; } + void SetMat( Matrix3& mat ) + { trot = mat * rot; } + RotateXForm(Quat q) { q.MakeMatrix(rot); } + }; + +class ScaleXForm : public XFormProc { + private: + Matrix3 scale, tscale; + public: + Point3 proc(Point3& p, Matrix3 &mat, Matrix3 &imat) + { return (p*tscale)*imat; } + void SetMat( Matrix3& mat ) + { tscale = mat*scale; } + ScaleXForm(Point3 s) { scale = ScaleMatrix(s); } + }; + + +class VertexDelta { + public: + Point3Tab deltas; //DS-- I needed this public + BitArray hide; + + void SetSize(int size, BOOL clear=TRUE); + Point3 operator[](int i); + void Set(TimeValue t,int i, Point3& p); + void Move(TimeValue t,int i, const Point3& p); + void Apply(TimeValue t,Mesh& mesh); + void Scale(float f); + VertexDelta& operator=(VertexDelta& from); + + ~VertexDelta(); + VertexDelta(); + }; + + +class TopoOp { + public: + virtual void Apply(Mesh& mesh)=0; + virtual TopoOp *Clone()=0; + virtual ~TopoOp() {} + }; + +typedef Tab TopoOpTab; + +class FaceMap { + public: + DWORD v[3]; + }; +typedef Tab FaceMapTab; + + +// Bits for attribs: +// First 3 bits are edge visibility +// 4th bit is face visibility +// 29,30 and 31 indicate which if any should be applied +#define ATTRIB_APPLY_EDGE (1<<31) +#define ATTRIB_APPLY_FACE (1<<30) +#define ATTRIB_APPLY_MATID (1<<29) +#define ATTRIB_APPLY_SMGROUP (1<<28) + +// Mat ID takes bit 5-21 +#define ATTRIB_MATID_SHIFT 5 +#define ATTRIB_MATID_MASK 0xffff + + +class TopoDelta { + public: + DWORDTab cverts; // Clone verts + DWORDTab dverts; // Delete verts + Point3Tab nverts; // Add verts + Point3Tab nTverts; // Add texture verts + DWORDTab dfaces; // Delete faces + Tab nfaces; // New faces + Tab nTVfaces; // New texture vert faces + FaceMapTab map; // Remap faces + FaceMapTab tmap; // TVFace remap + DWORDTab attribs; // Changes attributes of a face. + DWORDTab smgroups; // Changes smooth groups for a face. + DWORD inputFaces; // Number of faces on input mesh + DWORD inputVerts; // Number of vertices input mesh has + + }; + +class EditMeshData : public LocalModData { + public: + DWORD flags; + + // These record the changes to the incomming object. + VertexDelta vdelta; + TopoDelta tdelta; + + // The selection sets. + BitArray vsel; + BitArray fsel; + BitArray esel; + + // Lists of named selection sets + NamedSelSetList vselSet; + NamedSelSetList fselSet; + NamedSelSetList eselSet; + + // While an object is being edited, this exists. +// EMTempData *tempData; +}; + +inline float BPoly4(int i, float u) + { + float s = 1.0f-u; + switch (i) { + case 0: return s*s*s; + case 1: return 3.0f*u*s*s; + case 2: return 3.0f*u*u*s; + case 3: return u*u*u; + default: return 0.0f; + } + } +inline float BPoly3(int i, float u) + { + float s = 1.0f-u; + switch (i) { + case 0: return s*s; + case 1: return 2.0f*u*s; + case 2: return u*u; + default: return 0.0f; + } + } +inline float BPoly2(int i, float u) + { + switch (i) { + case 0: return 1.0f-u; + case 1: return u; + default: return 0.0f; + } + } + +#define EPSILON 0.001f + + +Point3 FfdLatticeSize(Box3 box) + { + Point3 size = box.Max()-box.Min(); + if (size.x==0.0f) size.x = 0.001f; + if (size.y==0.0f) size.y = 0.001f; + if (size.z==0.0f) size.z = 0.001f; + return size; + } + +Matrix3 FFDMod::CompTM(TimeValue t,INode *inode,ModContext *mc) + { + // Compute a the transformation out of lattice space into world space + Matrix3 ntm = inode->GetObjTMBeforeWSM(t); + Matrix3 ctm(1); + if (mc && mc->tm) { + ntm = Inverse(*(mc->tm)) * ntm; + } + tmControl->GetValue(t,&ctm,FOREVER,CTRL_RELATIVE); + return ctm * ntm; + } + +Box3 ComputeBBox(TimeValue t, INode *node) { + Box3 box; // The computed box + Matrix3 mat; // The Object TM + Matrix3 sclMat(1); // This will be used to apply the scaling + // Get the result of the pipeline at the current time + Object *obj = node->EvalWorldState(t).obj; + // Determine if the object is in world space or object space + // so we can get the correct TM. We can check this by getting + + if (node->GetObjTMAfterWSM(t).IsIdentity()) { + mat = Inverse(node->GetObjTMBeforeWSM(t)); + + } else { + // It's in object space, get the Object TM. + mat = node->GetObjectTM(t); + } + // Extract just the scaling part from the TM + AffineParts parts; + decomp_affine(mat, &parts); + //ApplyScaling(sclMat, ScaleValue(parts.k*parts.f, parts.u)); + // Get the bound box, and affect it by just + // the scaling portion + obj->GetDeformBBox(t, box, &sclMat); + return box; +} + +void AsciiExp::ExportFFD( INode* node , FFDMod *FfdMod, ModContext *mc, Mesh *mesh) +{ + TimeValue start = ip->GetAnimRange().Start(); + TimeValue end = ip->GetAnimRange().End(); + int delta = GetTicksPerFrame(); + int nbCtrlPnts; + TimeValue t; + int i; + int Depth; + int Width; + int Height; + + if (node->IsNodeHidden()) + return; + + if (FfdMod) { + + ObjectState os; + Object * obj; + + os = node->EvalWorldState( 0 ); + obj = node->GetObjectRef(); + while (obj && obj->SuperClassID() == GEN_DERIVOB_CLASS_ID) { + obj = ((IDerivedObject*)obj)->GetObjRef(); + } + + if (!obj) return; + + if (FfdMod->ClassID() == FFD44_CLASS_ID) { + Depth = Width = Height = 4; + } else + if (FfdMod->ClassID() == FFD33_CLASS_ID) { + Depth = Width = Height = 3; + } else + if (FfdMod->ClassID() == FFD22_CLASS_ID) { + Depth = Width = Height = 2; + } + + // Evaluate the TM controller + Matrix3 ctm(1); + FfdMod->tmControl->GetValue(start,&ctm,FOREVER,CTRL_RELATIVE); + + // Get the ModContext TM (if there is one) + Matrix3 tm = mc->tm ? *mc->tm : Matrix3(1); + + // Apply our TM to the MC TM + tm *= Inverse(ctm); + + // The origin of the TM is the lower left corner of the + // box, not the center. + tm.SetTrans(tm.GetTrans()-mc->box->Min()); + + // Compute scale factors to normalize lattice space + // to the extents of the box. + Point3 s = FfdLatticeSize(*mc->box); + + for (i=0; i<3; i++) { + if (s[i]==0.0f) s[i] = 1.0f; + else s[i] = 1.0f/s[i]; + } + tm.Scale(s,TRUE); + + Matrix3 itm = Inverse(tm); + + AffineParts ap; + decomp_affine(tm, &ap); + + nbCtrlPnts = FfdMod->NumCtrlPts(); + WriteChunkHdr( (char*)FFD_ID, 0); + fwrite( &nCurObj, sizeof( Uint32 ), 1, expStream ); + fwrite( &FfdNum, sizeof(Uint32), 1, expStream); + fwrite( &Width, sizeof( Uint32 ), 1, expStream ); + fwrite( &Height, sizeof( Uint32 ), 1, expStream ); + fwrite( &Depth, sizeof( Uint32 ), 1, expStream ); + + MRow *mr = tm.GetAddr(); + + fwrite( &mr[0][0], sizeof(MRow),1,expStream); + fwrite( &mr[1][0], sizeof(MRow),1,expStream); + fwrite( &mr[2][0], sizeof(MRow),1,expStream); + fwrite( &mr[3][0], sizeof(MRow),1,expStream); + + int dd,dw,dh; + int nv = obj->NumPoints(); + +/* for (i=0; iGetPoint(i); + if (pp.x>=-0.001f && pp.x<=1.001f && pp.y>=-0.001f && pp.y<=1.001f && pp.z>=-0.001f && pp.z<=1.001f) { + if (FfdMod->ClassID() == FFD44_CLASS_ID) { + w = BPoly4(dw,pp.x) * BPoly4(dd,pp.y) * BPoly4(dh,pp.z); + } else + if (FfdMod->ClassID() == FFD33_CLASS_ID) { + w = BPoly3(dw,pp.x) * BPoly3(dd,pp.y) * BPoly3(dh,pp.z); + } else + if (FfdMod->ClassID() == FFD22_CLASS_ID) { + w = BPoly2(dw,pp.x) * BPoly2(dd,pp.y) * BPoly2(dh,pp.z); + } + } else { + w = 0; + } + tw+=w; + } + } + } + }*/ + fprintf( tempStream, "node = %s\n\n", node->GetName() ); + fprintf( tempStream, "selected = "); + BitArray Selection = mesh->VertexTempSel(); + for (dw = 0; dwGetPoint(i); + //if (pp.x>=-0.0001f && pp.x<=1.0001f && pp.y>=-0.0001f && pp.y<=1.0001f && pp.z>=-0.0001f && pp.z<=1.0001f) { + if (Selection[i]) + { + if (FfdMod->ClassID() == FFD44_CLASS_ID) { + w = BPoly4(dw,pp.x) * BPoly4(dd,pp.y) * BPoly4(dh,pp.z); + } else + if (FfdMod->ClassID() == FFD33_CLASS_ID) { + w = BPoly3(dw,pp.x) * BPoly3(dd,pp.y) * BPoly3(dh,pp.z); + } else + if (FfdMod->ClassID() == FFD22_CLASS_ID) { + w = BPoly2(dw,pp.x) * BPoly2(dd,pp.y) * BPoly2(dh,pp.z); + } + fprintf( tempStream, "%d %f, ", i, w); + } else { + w = 0; + } + fwrite(&w, sizeof(float), 1, expStream); + } + fprintf( tempStream, "\nendselected\n"); + + } + } + } + + + WriteChunkHdr( (char*)ANIM_ID, 0); + fwrite( &CHANNEL_TYPE_FFD, sizeof( Uint32 ), 1, expStream ); + fwrite( &nCurObj, sizeof( Uint32 ), 1, expStream ); + fwrite( &totFrames, sizeof( Uint32 ), 1, expStream ); + fwrite( &FfdNum, sizeof(Uint32), 1, expStream); + fwrite( &Width, sizeof( Uint32 ), 1, expStream ); + fwrite( &Height, sizeof( Uint32 ), 1, expStream ); + fwrite( &Depth, sizeof( Uint32 ), 1, expStream ); + for (i=0; iGetObjTMBeforeWSM(t); + Matrix3 ctm(1); + if (mc && mc->tm) { + ntm = Inverse(*(mc->tm)) * ntm; + } + FfdMod->tmControl->GetValue(t,&ctm,FOREVER,CTRL_RELATIVE); + Matrix3 mm = ctm * ntm; + Box3 lbox = *mc->box; + Matrix3 stm = ScaleMatrix(FfdLatticeSize(lbox)); + stm.SetTrans(lbox.Min()); + + Point3 np = FfdMod->GetControlPoint(t,i); + Point3 p = stm*FfdMod->GetControlPoint(t, i); + Point3 p2 = mm * p; + fwrite(&p2.x, sizeof(float), 1, expStream); + fwrite(&p2.z, sizeof(float), 1, expStream); + fwrite(&p2.y, sizeof(float), 1, expStream); + } + } + + TimeValue start = ip->GetAnimRange().Start(); + TimeValue end = ip->GetAnimRange().End(); + int delta = GetTicksPerFrame(); + Control *c; + WriteChunkHdr( (char*)KEY_ID, 0); + fwrite( &CHANNEL_TYPE_FFD, sizeof( Uint32 ), 1, expStream ); + fwrite( &CHANNEL_INFO_ALL, sizeof( Uint32) , 1, expStream); + fwrite( &nCurObj, sizeof( Uint32), 1, expStream ); + fwrite( &FfdNum, sizeof(Uint32), 1, expStream); + fwrite( &nbCtrlPnts, sizeof( Uint32), 1, expStream ); + for (i=0; iGetPtCont(i); + if (c) { + ikeys = GetKeyControlInterface(c); + int blah = c->IsKeyable(); + if (ikeys) { + num = ikeys->GetNumKeys(); + if (num >0) + { + for (int j = 0; jClassID() == Class_ID(TCBINTERP_POINT3_CLASS_ID, 0)) { + ikeys->GetKey(j, &tcbPosKey); + time = tcbPosKey.time; + } else + if (c->ClassID() == Class_ID(HYBRIDINTERP_POINT3_CLASS_ID, 0)) { + ikeys->GetKey(j, &bezPosKey); + time = bezPosKey.time; + } + tl.Add(time); + } + } + } + } + if (!tl.Count()) { + tl.Add(ip->GetAnimRange().Start()); + } + num = tl.Count(); + + fwrite(&num, sizeof(int), 1, expStream); + for ( int j = 0; jGetObjTMBeforeWSM(time); + Matrix3 ctm(1); + if (mc && mc->tm) { + ntm = Inverse(*(mc->tm)) * ntm; + } + FfdMod->tmControl->GetValue(time,&ctm,FOREVER,CTRL_RELATIVE); + Matrix3 mm = ctm * ntm; + Box3 lbox = *mc->box; + Matrix3 stm = ScaleMatrix(FfdLatticeSize(lbox)); + stm.SetTrans(lbox.Min()); + + Point3 np = FfdMod->GetControlPoint(time,i); + Point3 p = stm*FfdMod->GetControlPoint(time, i); + Point3 p2 = mm * p; + + time = (time - start)/delta; + fwrite(&time, sizeof(int), 1, expStream); + fwrite(&p2.x, sizeof(float), 1, expStream); + fwrite(&p2.z, sizeof(float), 1, expStream); + fwrite(&p2.y, sizeof(float), 1, expStream); + } + } + } +} + +bool AsciiExp::FindFFDModifier (INode* node) +{ + // Get object from node. Abort if no object. + Object* ObjectPtr = node->GetObjectRef(); + FfdNum = 0; + bool Found= false; + + if (!ObjectPtr) + return false; + + // Is derived object ? + + int needDel; + + while (ObjectPtr->SuperClassID() == GEN_DERIVOB_CLASS_ID) + { + IDerivedObject* DerivedObjectPtr = static_cast(ObjectPtr); + ObjectPtr = DerivedObjectPtr->GetObjRef(); + } + TriObject * tri = GetTriObjectFromObject(ObjectPtr, ip->GetAnimRange().Start(), needDel); + if (!tri) return false; + + Mesh* mesh = &tri->mesh; + + BitArray SaveVtx = mesh->vertSel; + BitArray SaveFce = mesh->faceSel; + BitArray SaveEdg = mesh->edgeSel; + + ObjectPtr = node->GetObjectRef(); + while (ObjectPtr->SuperClassID() == GEN_DERIVOB_CLASS_ID) + { + // Yes -> Cast. + IDerivedObject* DerivedObjectPtr = static_cast(ObjectPtr); + + // Iterate over all entries of the modifier stack. + int ModStackIndex = DerivedObjectPtr->NumModifiers() - 1; + while (ModStackIndex >= 0 ) + { + // Get current modifier. + Modifier* ModifierPtr = DerivedObjectPtr->GetModifier(ModStackIndex); + ModContext *modContext = DerivedObjectPtr->GetModContext(ModStackIndex); + // Is this FFD ? + if (ModifierPtr->ClassID() == FFD44_CLASS_ID || + ModifierPtr->ClassID() == FFD33_CLASS_ID || + ModifierPtr->ClassID() == FFD22_CLASS_ID) + { + // Yes -> Exit. + ExportFFD(node, (FFDMod*)ModifierPtr, modContext, mesh); + FfdNum++; + Found = true; + } else + if (ModifierPtr->ClassID() == Class_ID(EDITMESH_CLASS_ID,0)) + { + EditMeshData *meshData = (EditMeshData*)modContext->localData; + mesh->vertSel = meshData->vsel; + mesh->faceSel = meshData->fsel; + mesh->edgeSel = meshData->esel; + } + + // Next modifier stack entry. + ModStackIndex--; + + + } + ObjectPtr = DerivedObjectPtr->GetObjRef(); + } + + mesh->vertSel = SaveVtx; + mesh->faceSel = SaveFce; + mesh->edgeSel = SaveEdg; + + if (needDel) delete tri; + + return Found; +} + diff --git a/Utils/GinExp/ExpFileIo.cpp b/Utils/GinExp/ExpFileIo.cpp new file mode 100644 index 000000000..a727edc1c --- /dev/null +++ b/Utils/GinExp/ExpFileIo.cpp @@ -0,0 +1,262 @@ +/*========================================================================= + + FILEIO.CPP + + Author: Tim Swann @ CLIMAX + Created: + Project: + Purpose: + + Copyright (c) 1998 Climax Development Ltd + +===========================================================================*/ + +/*---------------------------------------------------------------------- + Includes + -------- */ + +#include "AsciiExp.h" +#include "ExpFileIO.h" + + +/* Std Lib + ------- */ + +/* Glib + ---- */ + +/* Local + ----- */ + +/* Graphics + -------- */ + +/*---------------------------------------------------------------------- + Tyepdefs && Defines + ------------------- */ + +/*---------------------------------------------------------------------- + Structure defintions + -------------------- */ + +/*---------------------------------------------------------------------- + Positional Vars + --------------- */ + +/*---------------------------------------------------------------------- + Function Prototypes + ------------------- */ + +/*---------------------------------------------------------------------- + Vars + ---- */ + +/*---------------------------------------------------------------------- + Data + ---- */ + + +/*---------------------------------------------------------------------- + Function: + Purpose: + Params: + Returns: + ---------------------------------------------------------------------- */ + +void FIO_Stripfileinfo(char * Name, char * fname, char * ext, char * dir, char * drive) +{ + char tdrive[_MAX_DRIVE]; + char tdir[_MAX_DIR]; + char tfname[_MAX_FNAME]; + char text[_MAX_EXT]; + + + _splitpath(Name, tdrive, tdir, tfname, text); + + if (fname) + strcpy(fname, tfname); + + if (ext) + strcpy(ext, text); + + if (dir) + strcpy(dir, tdir); + + if (drive) + strcpy(drive, tdrive); + +} + + +/*---------------------------------------------------------------------- + Function: + Purpose: + Params: + Returns: + ---------------------------------------------------------------------- */ + +bool FIO_FileExists(const char * Name) +{ + int hFile; + + /* Open input file */ + hFile = _open(Name, _O_RDONLY | _O_BINARY); + + if(hFile != -1) + { + _close(hFile); // file close + return(TRUE); + } + else + { + return(FALSE); + } +} + + +/*---------------------------------------------------------------------- + Function: + Purpose: + Params: + Returns: + ---------------------------------------------------------------------- */ + +void * FIO_Read(char const * Name, int Offset) +{ + int hFile; + long filelen; + void * pAddr; + long pos; + + + /* Open input file */ + hFile = _open(Name, _O_RDONLY | _O_BINARY); + + if(hFile != -1) + { + filelen = FIO_FileLen(Name); // length of file + pAddr = malloc(filelen); // allocate mainmemory + + pos = _lseek( hFile, Offset, SEEK_SET ); + if( pos == -1 ) assert(!"File seek"); + filelen -= pos; + + _read(hFile, pAddr, filelen); // read in file + _close(hFile); // file close + } + else + { + assert(0); + } + return pAddr; +} + + +/*---------------------------------------------------------------------- + Function: + Purpose: + Params: + Returns: + ---------------------------------------------------------------------- */ + +bool FIO_ReadAtAddr(char const * Name, unsigned char * Dest, int Len, int Offset) +{ + int hFile; + long filelen; + long pos; + + + /* Open input file */ + hFile = _open(Name, _O_RDONLY | _O_BINARY); + + if(hFile != -1) + { + filelen = FIO_FileLen(Name); // length of file + + if (Len && (Len < filelen)) filelen = Len; + + pos = _lseek( hFile, Offset, SEEK_SET ); + if( pos == -1 ) assert(!"File seek"); + + _read(hFile, Dest, filelen); // read in file + _close(hFile); // file close + } + else + { + assert(0); + } + return TRUE; +} + + +/*---------------------------------------------------------------------- + Function: + Purpose: + Params: + Returns: + ---------------------------------------------------------------------- */ + +int FIO_FileLen(const char * Name) +{ + int hFile; + int filelen = 0; + + + /* Open input file */ + hFile = _open(Name, _O_RDONLY | _O_BINARY); + + if(hFile != -1) + { + filelen = _filelength(hFile); // length of file + _close(hFile); // file close + } + return filelen; +} + + +/*---------------------------------------------------------------------- + Function: + Purpose: + Params: + Returns: + ---------------------------------------------------------------------- */ + +bool FIO_Save(const char * Name,unsigned char * Addr,int Len) +{ + HANDLE hFile; + UINT fResult; + DWORD nWrited; + + + /* Open output file */ + hFile = CreateFile( Name, /* output filename */ + GENERIC_WRITE, /* mode = write */ + 0, /* share mode = not share */ + NULL, /* security attribute = cancel */ + CREATE_ALWAYS, /* create file always */ + FILE_ATTRIBUTE_NORMAL, /* file attribute = normal */ + NULL /* template file = non */ + ); + if(hFile) + { + fResult = WriteFile(hFile, /* IN : file handle */ + Addr, /* IN : pointer to write data */ + (DWORD)Len, /* IN : number of data in bytes */ + &nWrited, /* OUT : number of data just writed */ + NULL /* IN : pointer to OVERLAPPED structure */ + ); + CloseHandle(hFile); /* close file */ + + if(fResult == FALSE) + assert(!("SaveFile: ERROR Can not write file %s",Name)); + } + else + { + return FALSE; + } + return TRUE; +} + + + /*=========================================================================== + end */ diff --git a/Utils/GinExp/ExpFileIo.h b/Utils/GinExp/ExpFileIo.h new file mode 100644 index 000000000..962638c6d --- /dev/null +++ b/Utils/GinExp/ExpFileIo.h @@ -0,0 +1,63 @@ +/*========================================================================= + + FILEIO.H + + Author: Tim Swann @ CLIMAX + Created: + Project: + Purpose: + + Copyright (c) 1998 Climax Development Ltd + +===========================================================================*/ + +#ifndef __FILEIO__ +#define __FILEIO__ + +/*---------------------------------------------------------------------- + Includes + -------- */ + +/* Std Lib + ------- */ + +/* Glib + ---- */ + +/* Local + ----- */ + +/*---------------------------------------------------------------------- + Tyepdefs && Defines + ------------------- */ + +/*---------------------------------------------------------------------- + Structure defintions + -------------------- */ + +/*---------------------------------------------------------------------- + Globals + ------- */ + +/* Vars + ---- */ + +/* Data + ---- */ + +/* Functions + --------- */ + +void * FIO_Read(char const * Name, int Offset = 0); +bool FIO_ReadAtAddr(char const * Name,unsigned char * Dest,int Len = 0, int Offset = 0); +int FIO_FileLen(const char * Name); +bool FIO_FileExists(const char * Name); +bool FIO_Save(const char * Name,unsigned char * Addr,int Len); +void FIO_Stripfileinfo(char * Name, char * fname = NULL, char * ext = NULL, char * dir = NULL, char * drive = NULL); + +/*---------------------------------------------------------------------- */ + +#endif /* __FILEIO__ */ + +/*=========================================================================== + end */ diff --git a/Utils/GinExp/ExpMain.cpp b/Utils/GinExp/ExpMain.cpp new file mode 100644 index 000000000..9312fa924 --- /dev/null +++ b/Utils/GinExp/ExpMain.cpp @@ -0,0 +1,741 @@ +/*========================================================================= + + EXPMAIN.CPP + + Author: Tim Swann @ CLIMAX + Created: + Project: + Purpose: + + Copyright (c) 1998 Climax Development Ltd + +===========================================================================*/ + +/*---------------------------------------------------------------------- + Includes + -------- */ + +#include "Asciiexp.h" +#include "ExpFileIO.h" + + +/* Std Lib + ------- */ + +/* Glib + ---- */ + +/* Local + ----- */ + +/* Graphics + -------- */ + +/*---------------------------------------------------------------------- + Tyepdefs && Defines + ------------------- */ + +#define NINJAEXP_CLASS_ID Class_ID(0x689c7996, 0x13531d0d) + +/*---------------------------------------------------------------------- + Structure defintions + -------------------- */ + +/*---------------------------------------------------------------------- + Positional Vars + --------------- */ + +/*---------------------------------------------------------------------- + Function Prototypes + ------------------- */ + +/*---------------------------------------------------------------------- + Vars + ---- */ + +static HINSTANCE hInstance; +static AsciiExpClassDesc AsciiExpDesc; +static bool controlsInit = FALSE; + + +/*---------------------------------------------------------------------- + Data + ---- */ + +/*---------------------------------------------------------------------- + Function: + Purpose: + Params: + Returns: + ---------------------------------------------------------------------- */ + +BOOL WINAPI DllMain(HINSTANCE hinstDLL,ULONG fdwReason,LPVOID lpvReserved) +{ + hInstance = hinstDLL; + + // Initialize the custom controls. This should be done only once. + if (!controlsInit) + { + controlsInit = TRUE; + InitCustomControls(hInstance); + InitCommonControls(); + } + + return (TRUE); +} + + +/*---------------------------------------------------------------------- + Function: + Purpose: + Params: + Returns: + ---------------------------------------------------------------------- */ + +__declspec( dllexport ) const TCHAR* LibDescription() +{ + return GetString(IDS_LIBDESCRIPTION); +} + + +__declspec( dllexport ) int LibNumberClasses() +{ + return 1; +} + + +__declspec( dllexport ) ClassDesc* LibClassDesc(int i) +{ + switch(i) { + case 0: return GetAsciiExpDesc(); + default: return 0; + } +} + + +__declspec( dllexport ) ULONG LibVersion() +{ + return VERSION_3DSMAX; +} + + +/*---------------------------------------------------------------------- + Function: + Purpose: + Params: + Returns: + ---------------------------------------------------------------------- */ + +ClassDesc * GetAsciiExpDesc(void) +{ + return &AsciiExpDesc; +} + + +TCHAR * GetString(int id) +{ + static TCHAR buf[256]; + + if (hInstance) + return LoadString(hInstance, id, buf, sizeof(buf)) ? buf : NULL; + + return NULL; +} + + +/*---------------------------------------------------------------------- + Function: + Purpose: + Params: + Returns: + ---------------------------------------------------------------------- */ + +AsciiExp::AsciiExp() +{ +} + + +AsciiExp::~AsciiExp() +{ +} + + +int AsciiExp::ExtCount() +{ + return 1; +} + + +const TCHAR * AsciiExp::Ext(int n) +{ + switch(n) + { + case 0: + return _T("GIN"); + } + return _T(""); +} + + +const TCHAR * AsciiExp::LongDesc() +{ + return GetString(IDS_LONGDESC); +} + + +const TCHAR * AsciiExp::ShortDesc() +{ + return GetString(IDS_SHORTDESC); +} + + +const TCHAR * AsciiExp::AuthorName() +{ + return _T("Tim Swann / Mike Armstrong"); +} + + +const TCHAR * AsciiExp::CopyrightMessage() +{ + return GetString(IDS_COPYRIGHT); +} + + +const TCHAR * AsciiExp::OtherMessage1() +{ + return _T(""); +} + + +const TCHAR * AsciiExp::OtherMessage2() +{ + return _T(""); +} + + +unsigned int AsciiExp::Version() +{ + return 100; +} + + +static BOOL CALLBACK AboutBoxDlgProc(HWND hWnd, UINT msg, + WPARAM wParam, LPARAM lParam) +{ + switch (msg) { + case WM_INITDIALOG: + CenterWindow(hWnd, GetParent(hWnd)); + break; + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDOK: + EndDialog(hWnd, 1); + break; + } + break; + default: + return FALSE; + } + return TRUE; +} + + +void AsciiExp::ShowAbout(HWND hWnd) +{ +// DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, AboutBoxDlgProc, 0); +} + + +static BOOL CALLBACK ExportDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + AsciiExp * exp = (AsciiExp*)GetWindowLong(hWnd, GWL_USERDATA); + + switch (msg) + { + case WM_INITDIALOG: + exp = (AsciiExp*)lParam; + SetWindowLong(hWnd,GWL_USERDATA,lParam); + CenterWindow(hWnd, GetParent(hWnd)); +// CheckDlgButton(hWnd, IDC_ANIMCHECK, exp->GetExportAnimFlag()); + break; + + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDOK: +// exp->SetExportAnimFlag(IsDlgButtonChecked(hWnd, IDC_ANIMCHECK)); + EndDialog(hWnd, 1); + break; + + case IDCANCEL: + EndDialog(hWnd, 0); + break; + } + break; + default: + return FALSE; + } + return TRUE; +} + + +// Dummy function for progress bar +DWORD WINAPI fn(LPVOID arg) +{ + return(0); +} + + +/*---------------------------------------------------------------------- + Function: + Purpose: + Params: + Returns: + ---------------------------------------------------------------------- */ + +#if MAX_RELEASE == 2500 +int AsciiExp::DoExport(const TCHAR * name, ExpInterface * ei, Interface * i, BOOL suppressPrompts) +#else +int AsciiExp::DoExport(const TCHAR * name, ExpInterface * ei, Interface * i, BOOL suppressPrompts,DWORD options) +#endif +{ + int numChildren; +// NodeCount = 0; + + + // GRAB THE INTERFACE POINTER + ip = i; + MAX_hWnd = ip->GetMAXHWnd(); +// SetExportAnimFlag(0); +// if (!suppressPrompts && !DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_ASCIIEXPORT_DLG), MAX_hWnd, ExportDlgProc, (LPARAM)this)) return 1; + + tempStream = NULL; + expStream = NULL; + nbChunks = 0; + + + // GET SCENE FILENAME + sprintf( SceneName, "%s", FixupName(ip->GetCurFileName()) ); + _strupr( SceneName ); + + + // OPEN DEBUG TEXT FILE + tempStream = _tfopen("C:\\ExpDebug.txt",_T("wt")); + if (!tempStream) return 0; + + fprintf( tempStream, "TEST\n" ); + + // OPEN OUTPUT FILE + _splitpath(name, drive, dir, fname, ext); + sprintf(filename, "%s%s%s.GIN", drive, dir, fname); + _strlwr(filename); + expStream = _tfopen(filename, _T("wb")); + if (!expStream) return 0; + + // WRITE FILE HEADER + if (WriteChunkHdr( (char*)FILE_ID, 0 )) + { + // STARTUP THE PROGRESS BAR +// if (GetExportAnimFlag()) +// ip->ProgressStart("Exporting with Anims", TRUE, fn, NULL); +// else + ip->ProgressStart("Exporting without Vertex Anims", TRUE, fn, NULL); + + + Weights = NULL; + nTotalNodeCount = 0; + nCurNode = 0; + nCurObj = 0; + nbFrames = 0; + totFrames = 0; + nbWeights = 0; + NbEmmitters = 0; + + GetNoFrames(); + fwrite(&totFrames, sizeof(int), 1, expStream); + + WriteChunkHdr( (char*)VERSION_ID, 0); + int MyVer = VERSION; + fwrite(&MyVer, sizeof(int), 1, expStream); + + OrigProcess(ip->GetRootNode()); + FfdProcess(ip->GetRootNode(), nTotalNodeCount); + + ExportTree(); + + numChildren = ip->GetRootNode()->NumberOfChildren(); + for (int idx=0; idxGetCancel()) break; + nodeEnum( ip->GetRootNode()->GetChildNode(idx)); + } + + ExportMaterialList(); + } + + // WRITE FILE TERMINATOR HEADER + WriteChunkHdr( (char*)TERMINATOR_ID, 0 ); + + + // Close the streams + if (tempStream) fclose(tempStream); + if (expStream) fclose(expStream); + if (Weights) free(Weights); + + // FILL IN CHUNK SIZES + CalcChunkSizes(); + + // WE'RE DONE. FINISH THE PROGRESS BAR. + ip->ProgressEnd(); + +/* if (!DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_ENDDIALOG), MAX_hWnd, ExportDlgProc, (LPARAM)this)) + { + return 1; + }*/ + + + return 1; +} + + +/*---------------------------------------------------------------------- + Function: + Purpose: + Params: + Returns: + ---------------------------------------------------------------------- */ + +BOOL AsciiExp::nodeEnum(INode* node) +{ + nCurNode++; + + ip->ProgressUpdate((int)((float)nCurNode/nTotalNodeCount*100.0f)); + + if (ip->GetCancel()) return FALSE; + + ExportNode(node); + + for (int c = 0; c < node->NumberOfChildren(); c++) + { + if (!nodeEnum(node->GetChildNode(c))) return FALSE; + } + + return TRUE; +} + + +void AsciiExp::BoneProcess(INode* node, int& nodeCount) +{ + nodeCount++; + + // GET WEIGHTS BEFORE BONES ARE EXPORTED + ExportWeights( node ); + + for (int c = 0; c < node->NumberOfChildren(); c++) + { + BoneProcess(node->GetChildNode(c), nodeCount); + } +} + + +void AsciiExp::FfdProcess(INode* node, int& nodeCount) +{ + nodeCount++; + + // GET WEIGHTS BEFORE BONES ARE EXPORTED +// ExportFFD( node ); + Control *cont = node->GetTMController(); + if (cont) { + + if ((cont->ClassID() == BIPSLAVE_CONTROL_CLASS_ID) || + (cont->ClassID() == BIPBODY_CONTROL_CLASS_ID) || + (cont->ClassID() == FOOTPRINT_CLASS_ID)) + { + IBipedExport *BipIface = (IBipedExport *) cont->GetInterface(I_BIPINTERFACE); + if (BipIface) + { + //BipIface->RemoveNonUniformScale(1); + } + } + } + + for (int c = 0; c < node->NumberOfChildren(); c++) + { + FfdProcess(node->GetChildNode(c), nodeCount); + } +} + +void AsciiExp::OrigProcess(INode *node) +{ + char TempName[256]; + sprintf(TempName, "%s", node->GetName()); + _strupr(TempName); + if (strcmp(TempName, "ORIGIN")==0) { + Matrix3 tm = node->GetObjTMAfterWSM( ip->GetAnimRange().Start() ); + AffineParts ap; + decomp_affine(tm, &ap); + WriteChunkHdr( (char*)ORIG_ID, 0); + fwrite(&ap.t.x, sizeof(float), 1, expStream); + fwrite(&ap.t.z, sizeof(float), 1, expStream); + fwrite(&ap.t.y, sizeof(float), 1, expStream); + } + for (int c = 0; c < node->NumberOfChildren(); c++) + { + OrigProcess(node->GetChildNode(c)); + } +} + + +void AsciiExp::GetNoFrames( void ) +{ + TimeValue start = ip->GetAnimRange().Start(); + TimeValue end = ip->GetAnimRange().End(); + TimeValue t; + int delta = GetTicksPerFrame(); + Object * obj = ip->GetRootNode()->EvalWorldState(ip->GetAnimRange().Start()).obj; + + totFrames = 0; + for (t=start; t<=end; t+=delta) totFrames++; +} + + +/*---------------------------------------------------------------------- + Function: + Purpose: + Params: + Returns: + ---------------------------------------------------------------------- */ + +void AsciiExp::ExportNode(INode* node) +{ + ObjectState os = node->EvalWorldState(ip->GetAnimRange().Start()); + + OutputDebugString(node->GetName()); + + if (os.obj) + { + switch(os.obj->SuperClassID()) + { + case GEOMOBJECT_CLASS_ID: + ip->ProgressStart("Export Model", TRUE, fn, NULL); + ExportModel( node ); + break; + case HELPER_CLASS_ID: + ip->ProgressStart("Export Helper", TRUE, fn, NULL); + break; + case CAMERA_CLASS_ID: + ip->ProgressStart("Export Camera", TRUE, fn, NULL); + ExportCamera( node ); + break; + case LIGHT_CLASS_ID: + ip->ProgressStart("Export Light", TRUE, fn, NULL); + ExportLight(node); + break; + } + } + ExportProp(node); +} + +/*---------------------------------------------------------------------- + Function: Misc Utility functions + Purpose: + Params: + Returns: + ---------------------------------------------------------------------- */ + +/*---------------------------------------------------------------------- + Function: Misc Utility functions + Purpose: + Params: + Returns: + ---------------------------------------------------------------------- */ + +static Point3 basic_tva[3] = +{ + Point3(0.0,0.0,0.0),Point3(1.0,0.0,0.0),Point3(1.0,1.0,0.0) +}; + +static Point3 basic_tvb[3] = +{ + Point3(1.0,1.0,0.0),Point3(0.0,1.0,0.0),Point3(0.0,0.0,0.0) +}; + +static int nextpt[3] = {1,2,0}; +static int prevpt[3] = {2,0,1}; + + + +BOOL AsciiExp::TMNegParity(Matrix3 &m) +{ + return (DotProd(CrossProd(m.GetRow(0),m.GetRow(1)),m.GetRow(2))<0.0)?1:0; +} + + +TriObject * AsciiExp::GetTriObjectFromNode(INode *node, TimeValue t, int &deleteIt) +{ + deleteIt = FALSE; + + Object *obj = node->EvalWorldState(t).obj; + + ObjectState os = obj->Eval(t); + obj = os.obj; + + if (obj && obj->CanConvertToType(Class_ID(TRIOBJ_CLASS_ID, 0))) + { + TriObject *tri = (TriObject *) obj->ConvertToType(t, Class_ID(TRIOBJ_CLASS_ID, 0)); + if (obj != tri) deleteIt = TRUE; + + return tri; + } + else + { + return NULL; + } +} + +TriObject * AsciiExp::GetTriObjectFromObject(Object *obj, TimeValue t, int &deleteIt) +{ + deleteIt = FALSE; + + if (obj && obj->CanConvertToType(Class_ID(TRIOBJ_CLASS_ID, 0))) + { + TriObject *tri = (TriObject *) obj->ConvertToType(t, Class_ID(TRIOBJ_CLASS_ID, 0)); + if (obj != tri) deleteIt = TRUE; + + return tri; + } + else + { + return NULL; + } +} + +void AsciiExp::make_face_uv(Face *f, Point3 *tv) +{ + int na,nhid,i; + Point3 *basetv; + + /* make the invisible edge be 2->0 */ + nhid = 2; + if (!(f->flags&EDGE_A)) nhid=0; + else if (!(f->flags&EDGE_B)) nhid = 1; + else if (!(f->flags&EDGE_C)) nhid = 2; + na = 2-nhid; + basetv = (f->v[prevpt[nhid]]v[nhid]) ? basic_tva : basic_tvb; + for (i=0; i<3; i++) + { + tv[i] = basetv[na]; + na = nextpt[na]; + } +} + +/*---------------------------------------------------------------------- + Function: + Purpose: + Params: + Returns: + ---------------------------------------------------------------------- */ + +bool AsciiExp::WriteChunkHdr( char* ChunkID, long Size ) +{ + + SizeTab[nbChunks] = ftell( expStream ); + if (SizeTab[nbChunks] & 0x3) assert(0); + //SizeTab[nbChunks] /= 4; + + int sl = strlen(ChunkID) + 1; + if (sl&3) { + sl += 4 - (sl & 3); + } + + fwrite( ChunkID, sl, 1, expStream ); + fwrite( &Size, sizeof(Uint32), 1, expStream ); + + nbChunks++; + if(nbChunks >= MAX_FILE_CHUNKS) + { + MessageBox( MAX_hWnd, "INCREASE MAX_FILE_CHUNKS", "ERROR", MB_OK); + return false; + } + return true; +} + + +void AsciiExp::CalcChunkSizes( void ) +{ + int hFile; + long filelen; + char * filebuf; + + + hFile = _open(filename, _O_RDONLY | _O_BINARY); + + if(hFile != -1) + { + filelen = FIO_FileLen(filename); // length of file + filebuf = (char *)malloc(filelen); // allocate mainmemory + + _read(hFile, filebuf, filelen); // read in file + _close(hFile); // file close + } + else + { + assert(0); + } + + for (int i=0;i<(nbChunks-1);i++) + { + long offset; + long filepos; + + filepos = SizeTab[i]; + int sl = strlen(&filebuf[filepos]) + 1; + if (sl&3) { + sl += 4 - (sl & 3); + } + filepos += sl; + offset = (SizeTab[i+1] - filepos - 4); + //offset -= 8; // DON'T INCLUDE CHUNK HDR IN CHUNK SIZE + + *(long*)&filebuf[filepos] = offset; + } + + FIO_Save( filename, (unsigned char *)filebuf, filelen); + + free( filebuf ); + + if(expStream) fclose( expStream); +} + + +/*---------------------------------------------------------------------- + Function: String manipulation functions + Purpose: + Params: + Returns: + ---------------------------------------------------------------------- */ + +#define CTL_CHARS 40 +#define SINGLE_QUOTE 39 + +TCHAR* AsciiExp::FixupName(TCHAR* name) +{ + static char buffer[256]; + TCHAR* cPtr; + + _tcscpy(buffer, name); + cPtr = buffer; + + while(*cPtr) + { + if (*cPtr <= CTL_CHARS || *cPtr == '-') + *cPtr = _T('_'); + cPtr++; + } + + return buffer; +} + + +/*=========================================================================== + end */ diff --git a/Utils/GinExp/ExpMaterial.cpp b/Utils/GinExp/ExpMaterial.cpp new file mode 100644 index 000000000..0de6a6bd4 --- /dev/null +++ b/Utils/GinExp/ExpMaterial.cpp @@ -0,0 +1,518 @@ +/*========================================================================= + + EXPMATERIAL.CPP + + Author: Tim Swann @ CLIMAX + Created: + Project: + Purpose: + + Copyright (c) 1998 Climax Development Ltd + +===========================================================================*/ + +/*---------------------------------------------------------------------- + Includes + -------- */ + +#include "AsciiExp.h" +#include "string.h" + +/* Std Lib + ------- */ + +/* Glib + ---- */ + +/* Local + ----- */ + +/* Graphics + -------- */ + +/*---------------------------------------------------------------------- + Tyepdefs && Defines + ------------------- */ + +/*---------------------------------------------------------------------- + Structure defintions + -------------------- */ + +/*---------------------------------------------------------------------- + Positional Vars + --------------- */ + +/*---------------------------------------------------------------------- + Function Prototypes + ------------------- */ + +/*---------------------------------------------------------------------- + Vars + ---- */ + +/*---------------------------------------------------------------------- + Data + ---- */ + +/*---------------------------------------------------------------------- + Function: + Purpose: + Params: + Returns: + ---------------------------------------------------------------------- */ + +void AsciiExp::ExportMaterialList(void) +{ + int numMtls = mtlList.Count(); + int i = 0; + + + nbTexs = 0; + nbMats = 0; + + if (numMtls) + { + // EXPORT TEXTURE FILE LIST + for (i=0; i= 0; i--) + if (temp1[i] == '\\') + break; + else + temp1[i] = 0; + + strcpy(temp2, newpath); + for (i = strlen(temp2) - 1; i >= 0; i--) + if (temp2[i] == '\\') + break; + else + temp2[i] = 0; + + // Are the paths the same? + strcpy(outstr, ""); + if (strcmp(temp1, temp2) == 0) // Paths are the same + { + j = strlen(temp1); + } + else // Paths are different + { + j = 2; // Drives are the same + // Find different bits + for (i = 0; i < (int) strlen(temp1); i++) + { + if ((temp1[i] == '\\') && (temp2[i] == '\\')) + j = i + 1; + else + if (temp1[i] != temp2[i]) + break; + } + if (j > 3) + { + for (i = j; i < (int) strlen(temp1); i++) + if (temp1[i] == '\\') + strcat(outstr,"..\\"); + } + else + j = 2; + } + strcat(outstr, &newpath[j]); + } + return outstr; +} + +bool FileExists(const char* Name) +{ + FILE *f = fopen(Name,"rb"); + if (f) + { + fclose(f); + return true; + } + return false; +} + +TSTR AsciiExp::FindTextureFName(TSTR MapName) +{ + char Out[256]; + _splitpath(filename, drive, dir, fname, ext); + TSTR GinDir = TSTR(drive)+TSTR(dir); + _splitpath(MapName, drive, dir, fname, ext); + TSTR DriveDir = TSTR(drive)+TSTR(dir); + TSTR FName = TSTR(fname) + TSTR(ext); + + if (!FileExists(GinDir + FName)) + { + MapName = ip->GetCurFilePath(); + _splitpath(MapName, drive, dir, fname, ext); + DriveDir = TSTR(drive)+TSTR(dir); + if (!FileExists(GinDir + FName)) + { + int i=0; + while (iGetMapDirCount()) + { + DriveDir = TheManager->GetMapDir(i); + DriveDir = DriveDir + TSTR("\\"); + if (FileExists(DriveDir + FName)) { + break; + } + i++; + } + if (i==TheManager->GetMapDirCount()) + { + DriveDir = TSTR(""); + } + } + } + GinDir.toLower(); + DriveDir.toLower(); + makerelative(GinDir, DriveDir, Out); + return TSTR(Out); +} + +void AsciiExp::DumpTexture( Mtl* mtl, int matno, int subno ) +{ + Texmap * subTex = mtl->GetSubTexmap(ID_DI); + + TSTR N = mtl->GetName(); + if (mtl->ClassID() == Class_ID(DMTL_CLASS_ID, 0)) + { + if (subTex && subTex->ClassID() == Class_ID(BMTEX_CLASS_ID, 0)) + { + // If it is a standard material we can see if the map is enabled. + if (((StdMat*)mtl)->MapEnabled(ID_DI)) + { + Bool found = FALSE; + int texidx = nbTexs; + char TexListTemp[256]; + + + TSTR mapName = ((BitmapTex *)subTex)->GetMapName(); + TSTR RelativePath = FindTextureFName(mapName); + _splitpath(mapName, drive, dir, fname, ext); + + if (RelativePath.isNull()) + { + sprintf( TexListTemp, "%s%s", fname, ext); + } else + { + sprintf( TexListTemp, "%s%s%s", RelativePath, fname, ext); + } + + for (int t=0;t= MAXCOUNT) MessageBox( MAX_hWnd, "nbTexs Overflow", "ERROR", MB_OK); + } + MatList[matno].SubTex[(subno+1)] = texidx; + } + } + } + + + if (mtl->NumSubMtls() > 0) + { + for (int i=0; iNumSubMtls(); i++) + { + Mtl * subMtl = mtl->GetSubMtl(i); + if (subMtl) + { + DumpTexture( subMtl, matno, i ); + } + } + } +} + +int AsciiExp::getTextureId( Mtl *mtl ) +{ + Texmap * subTex = mtl->GetSubTexmap(ID_DI); + + TSTR N = mtl->GetName(); + if (mtl->ClassID() == Class_ID(DMTL_CLASS_ID, 0)) + { + if (subTex && subTex->ClassID() == Class_ID(BMTEX_CLASS_ID, 0)) + { + // If it is a standard material we can see if the map is enabled. + if (((StdMat*)mtl)->MapEnabled(ID_DI)) + { + Bool found = FALSE; + int texidx = nbTexs; + char TexListTemp[256]; + + + TSTR mapName = ((BitmapTex *)subTex)->GetMapName(); + TSTR RelativePath = FindTextureFName(mapName); + _splitpath(mapName, drive, dir, fname, ext); + + fprintf( tempStream, " texName = %s\n", fname ); + if (RelativePath.isNull()) + { + sprintf( TexListTemp, "%s%s", fname, ext); + } else + { + sprintf( TexListTemp, "%s%s%s", RelativePath, fname, ext); + } + + for (int t=0;t= MAXCOUNT) MessageBox( MAX_hWnd, "nbTexs Overflow", "ERROR", MB_OK); + + return nbTexs-1; + } + } + } + } + return -1; +} + +/*---------------------------------------------------------------------- + ---------------------------------------------------------------------- */ + +void AsciiExp::DumpMaterial( Mtl* mtl, int matno, int subno , bool recurse) +{ + Color col; + float alpha; + Uint32 AttrFlags = 0; + + + if (!mtl) return; + + + sprintf( MatList[nbMats].name, "%s", FixupName( mtl->GetName() ) ); + + fprintf( tempStream, "matName = %s %d\n", mtl->GetName(), nbMats ); + // WE KNOW THE STANDARD MATERIAL, SO WE CAN GET SOME EXTRA INFO + if (mtl->ClassID() == Class_ID(DMTL_CLASS_ID, 0)) + { + StdMat * std; + + std = (StdMat*)mtl; + + + int tmap = getTextureId(mtl); + if (std->MapEnabled(ID_DI) && tmap != -1) AttrFlags |= GIN_FLAG_USE_TEXTURE; + if (std->MapEnabled(ID_RL)) AttrFlags |= GIN_FLAG_USE_ENVTEXTURE; + if (std->GetTwoSided()) AttrFlags |= GIN_FLAG_DOUBLE_SIDE; + if (std->GetSelfIllum( 0 ) > 0.2f ) AttrFlags |= GIN_FLAG_IGNORE_LIGHT; + AttrFlags |= (std->GetTransparencyType()<<3); + + col = std->GetDiffuse( 0 ); + alpha = std->GetOpacity( 0 ); + if (std->MapEnabled(ID_DI)) { + MatList[nbMats].material.diffuse.alpha = alpha; + MatList[nbMats].material.diffuse.colour = col; + } else { + MatList[nbMats].material.diffuse.alpha = alpha; + MatList[nbMats].material.diffuse.colour = col; + } + + col = std->GetSpecular( 0 ); + MatList[nbMats].material.specular.alpha = alpha; + MatList[nbMats].material.specular.colour = col; + + MatList[nbMats].material.exponent = 6.f; + + fprintf( tempStream, " tmap = %d\n\n", tmap ); + MatList[nbMats].material.tex_flags = tmap; + MatList[nbMats].material.attr_flags = AttrFlags; + + nbMats++; + + } else + { + if (mtl->ClassID() != Class_ID(MULTI_CLASS_ID, 0)) + { + memset(&MatList[nbMats], 0, sizeof(MATLIST)); + MatList[nbMats].material.tex_flags = -1; + nbMats++; + } + } + + if (mtl->NumSubMtls() > 0) + { + int c = (recurse) ? 1 : mtl->NumSubMtls(); + for (int i=0; i< c ; i++) + { + Mtl* subMtl = mtl->GetSubMtl(i); + if (subMtl) + { + DumpMaterial( subMtl, matno, i , true); + } else + { + nbMats++; + } + } + } + if(nbMats>= MAXCOUNT) MessageBox( MAX_hWnd, "nbMats Overflow", "ERROR", MB_OK); + +} + + +/*---------------------------------------------------------------------- + Function: + Purpose: + Params: + Returns: + ---------------------------------------------------------------------- */ +int MtlKeeper::GetSubMtlCount(Mtl* mtl, bool recurse) +{ + int Count = 0; + if (!mtl) + return 0; + if (mtl->ClassID() == Class_ID(DMTL_CLASS_ID, 0)) + Count ++; + if (mtl->NumSubMtls() > 0) + { + int c = (recurse) ? 1 : mtl->NumSubMtls(); + for (int i=0; iGetSubMtl(i); + if (subMtl) + { + Count += GetSubMtlCount( subMtl, true); + } else + { + Count ++; + } + } + } + return Count; +} + +BOOL MtlKeeper::AddMtl(Mtl* mtl) +{ + if (!mtl) return FALSE; + + int numMtls = mtlTab.Count(); + for (int i=0; iEvalWorldState( ip->GetAnimRange().Start() ); + char nodeName[256]; + + // Targets are actually geomobjects, but we will export them + // from the camera and light objects, so we skip them here. + if (os.obj->ClassID() == Class_ID(TARGET_CLASS_ID, 0)) return; + ip->ProgressUpdate(0,0,"Export Model0"); + + sprintf(nodeName, "%s", node->GetName()); + strupr(nodeName); + if (nodeName[0] != 'R' || + nodeName[1] != 'O' || + nodeName[2] != 'O' || + nodeName[3] != 'T') + { + ip->ProgressUpdate(0,0,"Export ModelRoot"); + WriteChunkHdr( (char*)OBJ_ID, 0 ); + fwrite(&totFrames, sizeof(int), 1, expStream); + fwrite(nodeName, MAX_NAME_LENGTH, 1, expStream); + + ip->ProgressUpdate(0,0,"Export Model-Mesh"); + ExportMesh( node ); + } + +} + + +/*---------------------------------------------------------------------- + Function: + Purpose: + Params: + Returns: + ---------------------------------------------------------------------- */ + +void AsciiExp::ExportMesh( INode* node ) +{ + + Matrix3 tm = node->GetObjTMAfterWSM( ip->GetAnimRange().Start() ); + Uint32 EvalFlags = 0; + Point3 ObjPos; + Point3 ObjRot; + Point3 ObjSca; + Point3 center = Point3(0.f, 0.f, 0.f); + AffineParts ap; + Mesh * mesh; + ObjectState os; + Object * obj; + BOOL needDel; + TriObject * tri; + + os = node->EvalWorldState( ip->GetAnimRange().Start() ); + obj = node->EvalWorldState( ip->GetAnimRange().Start() ).obj; + + Mtl * nodeMtl = node->GetMtl(); + if (!nodeMtl) return; + + if (!os.obj) return; + if (!obj->IsRenderable()) return; + if (node->IsNodeHidden()) return; + if (!obj || os.obj->SuperClassID() != GEOMOBJECT_CLASS_ID) return; // Safety net. This shouldn't happen. + + tri = GetTriObjectFromNode(node, ip->GetAnimRange().Start(), needDel); + if (!tri) return; + + mesh = &tri->mesh; + + mtlList.AddMtl(nodeMtl); + + ip->ProgressUpdate(0,0,"ExportMeshList"); + ExportMeshList( node, mesh ); + ip->ProgressUpdate(0,0,"ExportPoints"); + ExportPoints( node, mesh ); + ip->ProgressUpdate(0,0,"ExportVectors"); + ExportVectors( node, mesh); + ip->ProgressUpdate(0,0,"ExportVectorsDone"); + + if (needDel) delete tri; + + tri = GetTriObjectFromNode(node, ip->GetAnimRange().End(), needDel); + if (!tri) return; + + mesh = &tri->mesh; + ip->ProgressUpdate(0,0,"ExportNormals"); + ExportNormals( node, mesh ); + if (needDel) delete tri; + ip->ProgressUpdate(0,0,"ExportNormalsDone"); + + tm = node->GetObjTMAfterWSM(ip->GetAnimRange().Start());// * Inverse(node->GetParentTM(t)); + decomp_affine(tm, &ap); + + ip->ProgressUpdate(0,0,"WriteHeader0"); + WriteChunkHdr( (char*)MODEL_ID, 0 ); + fwrite( &nCurObj, sizeof(long), 1, expStream ); +char NodeName[MAX_NAME_LENGTH]; + sprintf(NodeName,"%s",node->GetName()); +// fwrite( node->GetName(), MAX_NAME_LENGTH, 1, expStream); + fwrite( NodeName, MAX_NAME_LENGTH, 1, expStream); + fwrite( &radius, sizeof(float), 1, expStream ); + fwrite( ¢er.x, sizeof(float), 1, expStream ); + fwrite( ¢er.y, sizeof(float), 1, expStream ); + fwrite( ¢er.z, sizeof(float), 1, expStream ); + fwrite( &ap.t.x, sizeof(float), 1, expStream ); + fwrite( &ap.t.z, sizeof(float), 1, expStream ); + fwrite( &ap.t.y, sizeof(float), 1, expStream ); +//ProgressUpdate(int pct, BOOL showPct = TRUE, TCHAR *title = NULL)=0 + + ip->ProgressUpdate(0,0,"WriteHeaderDone"); + + ip->ProgressUpdate(0,0,"FindPhysiqueModifier"); + if (FindPhysiqueModifier(node)) + { + ip->ProgressUpdate(0,0,"ExportWeights"); + ExportWeights(node); + } + else + ip->ProgressUpdate(0,0,"FindFFDModifier"); + if (FindFFDModifier(node)) + { + } + else + { + ip->ProgressUpdate(0,0,"ExportNodeAnimMesh"); + ExportNodeAnimMesh( node ); + } + +// ExportAnimMesh( node ); + ip->ProgressUpdate(0,0,"FindEditableMesh"); + FindEditableMesh(node); + ip->ProgressUpdate(0,0,"ExportVisTrack"); + ExportVisTrack( node ); + nCurObj++; +} + +void AsciiExp::FindEditableMesh(INode *node) +{ + Object *o = node->GetObjectRef(); + while (o && o->SuperClassID() == GEN_DERIVOB_CLASS_ID) + { + o = ((IDerivedObject*)o)->GetObjRef(); + } + if (o && o->ClassID() == Class_ID(EDITTRIOBJ_CLASS_ID,0)) + { + ExportMeshVertAnim(node, (EditTriObject*)o); + } +} + +void AsciiExp::ExportMeshVertAnim(INode *node, EditTriObject *e) +{ + TimeValue start = ip->GetAnimRange().Start(); + TimeValue end = ip->GetAnimRange().End(); + int delta = GetTicksPerFrame(); + + int Controls = e->cont.Count(); + + + Matrix3 tm = node->GetObjTMAfterWSM( ip->GetAnimRange().Start() ); + Point3 p; + + Controls = 0; + for (int i=0; icont.Count(); i++) { + if (e->cont[i]) { + Controls++; + } + } + + ITCBPoint3Key tcbPosKey; + IBezPoint3Key bezPosKey; + + WriteChunkHdr( (char*)KEY_ID, 0); + fwrite( &CHANNEL_TYPE_VERTEX, sizeof( Uint32 ), 1, expStream ); + fwrite( &CHANNEL_INFO_POSITION, sizeof( Uint32) , 1, expStream); + fwrite( &nCurObj, sizeof( Uint32 ), 1, expStream ); + fwrite( &Controls, sizeof(int), 1, expStream); + + for (i=0; icont.Count(); i++) { + if (e->cont[i]) { + Control *c = GetControlInterface(e->cont[i]); + IKeyControl *ikeys; + TimeList tl; + TimeValue time; + tl.Clear(); + if (c) { + ikeys = GetKeyControlInterface(c); + int num = ikeys->GetNumKeys(); + for (int j = 0; jClassID() == Class_ID(TCBINTERP_POINT3_CLASS_ID, 0)) { + ikeys->GetKey(j, &tcbPosKey); + time = tcbPosKey.time; + } + else + if (c->ClassID() == Class_ID(HYBRIDINTERP_POINT3_CLASS_ID, 0)) + { + ikeys->GetKey(j, &bezPosKey); + time = bezPosKey.time; + } + tl.Add(time); + } + num = tl.Count(); + fwrite(&i, sizeof(int), 1, expStream); + fwrite(&num, sizeof(int), 1, expStream); + for (j=0; jGetObjTMAfterWSM(time); + e->cont[i]->GetValue(time,&p,FOREVER); + p = tm * p; + time = (time-start) / delta; + fwrite(&time, sizeof(int), 1, expStream); + fwrite(&p.x, sizeof(float), 1, expStream); + fwrite(&p.z, sizeof(float), 1, expStream); + fwrite(&p.y, sizeof(float), 1, expStream); + } + } + } + } +} + +/*---------------------------------------------------------------------- + ---------------------------------------------------------------------- */ +int GetSubMtlCount(Mtl* mtl, bool recurse = false) +{ + int Count = 0; + if (!mtl) + return 0; + if (mtl->ClassID() == Class_ID(DMTL_CLASS_ID, 0)) + Count ++; + + if (mtl->NumSubMtls() > 0) + { + int c = (recurse) ? 1 : mtl->NumSubMtls(); + for (int i=0; iGetSubMtl(i); + if (subMtl) + { + Count += GetSubMtlCount( subMtl, true); + } + } + } + return Count; +} + +void AsciiExp::ExportMeshList(INode * node, Mesh * mesh) +{ + MtlID matid; + MtlID matref; + Mtl * nodeMtl = node->GetMtl(); + int m; + ObjectState os; + Object * obj; + Matrix3 tm = node->GetObjTMAfterWSM( ip->GetAnimRange().Start() ); + BOOL negScale = TMNegParity(tm); + int vx1, vx2, vx3; + const char * name = node->GetName(); + + +// fprintf( tempStream, "MESHLIST = %s\n\n", name ); + + obj = node->EvalWorldState( ip->GetAnimRange().Start() ).obj; + mesh->buildNormals(); + + // Order of the vertices. Get 'em counter clockwise if the objects is + // negatively scaled. + if (negScale) + { + vx1 = 2; + vx2 = 1; + vx3 = 0; + } + else + { + vx1 = 0; + vx2 = 1; + vx3 = 2; + } + + int MtlCount = GetSubMtlCount(nodeMtl); + if (MtlCount == 0) return; + // Find all mesh materials used + nbMeshs = 0; + for (int i=0; igetNumFaces(); i++) + { + Bool found = FALSE; + + matid = ((unsigned long)mesh->getFaceMtlIndex(i)) % MtlCount; + +// fprintf( tempStream, "MATID = %d - FACEID = %d\n", mesh->faces[i].getMatID(), mesh->getFaceMtlIndex(i) ); + + for (int f=0;f= MAXCOUNT) MessageBox( MAX_hWnd, "nbMeshs Overflow", "ERROR", MB_OK); + + } + } + + + // Sort material id's for ease of access + + for (m=0;m MeshSet[m2+1].MatId) + { + MESH_SET meshset = MeshSet[m2]; + MeshSet[m2] = MeshSet[m2+1]; + MeshSet[m2+1] = meshset; + } + } + } + + + for (m=0;mgetNumFaces(); i++) + { + matid = ((unsigned long)mesh->getFaceMtlIndex(i)) % MtlCount; + if (matid == MeshSet[m].MatId) + { + fwrite( &mesh->faces[i].v[vx1], sizeof(long), 1, expStream ); + fwrite( &mesh->faces[i].v[vx2], sizeof(long), 1, expStream ); + fwrite( &mesh->faces[i].v[vx3], sizeof(long), 1, expStream ); +long HF0=mesh->faces[i].getEdgeVis(vx1); +long HF1=mesh->faces[i].getEdgeVis(vx2); +long HF2=mesh->faces[i].getEdgeVis(vx3); + fwrite( &HF0, sizeof(long), 1, expStream ); + fwrite( &HF1, sizeof(long), 1, expStream ); + fwrite( &HF2, sizeof(long), 1, expStream ); +// fprintf( tempStream, "Face = %d - HF= %d %d %d\n", i,HF0,HF1,HF2); + } + } + + + // EXPORT FACE MAP TEXCOORDS IF WE HAVE THEM... + if (obj->HasUVW()) + { + if (!CheckForAndExportFaceMap(nodeMtl, mesh, m)) + { + // If not, export standard tverts + int numTVx = mesh->getNumTVerts(); + + if (numTVx) + { + int nbFaces; + + WriteChunkHdr( (char*)UV_ID, 0 ); + fwrite( &nCurObj, sizeof(Uint16), 1, expStream ); + fwrite( &m, sizeof(Uint16), 1, expStream ); + nbFaces = MeshSet[m].nbFaces; + fwrite( &nbFaces, sizeof(Uint32), 1, expStream ); + + for (i=0; igetNumFaces(); i++) + { + matid = ((unsigned long)mesh->getFaceMtlIndex(i)) % MtlCount; + + if (matid == MeshSet[m].MatId) + { + UVVert tv; + int idx; + + idx = mesh->tvFace[i].t[vx1]; + tv = mesh->tVerts[idx]; + fwrite(&tv.x, sizeof(float), 1, expStream); + fwrite(&tv.y, sizeof(float), 1, expStream); + + idx = mesh->tvFace[i].t[vx2]; + tv = mesh->tVerts[idx]; + fwrite(&tv.x, sizeof(float), 1, expStream); + fwrite(&tv.y, sizeof(float), 1, expStream); + + idx = mesh->tvFace[i].t[vx3]; + tv = mesh->tVerts[idx]; + fwrite(&tv.x, sizeof(float), 1, expStream); + fwrite(&tv.y, sizeof(float), 1, expStream); + } + } + } + } + } + { + int nbFaces; + + WriteChunkHdr( (char*)FACENORMAL_ID, 0 ); + fwrite( &nCurObj, sizeof(Uint16), 1, expStream ); + fwrite( &m, sizeof(Uint16), 1, expStream ); + nbFaces = MeshSet[m].nbFaces; + fwrite( &nbFaces, sizeof(Uint32), 1, expStream ); + + for (i=0; igetNumFaces(); i++) + { + matid = ((unsigned long)mesh->getFaceMtlIndex(i)) % MtlCount; + if (matid == MeshSet[m].MatId) + { + Point3 n=GetVertexNormal(mesh, i,mesh->getRVertPtr(mesh->faces[i].v[vx1])); + fwrite(&n.x, sizeof(float), 1, expStream); + fwrite(&n.y, sizeof(float), 1, expStream); + fwrite(&n.z, sizeof(float), 1, expStream); + + n=GetVertexNormal(mesh, i,mesh->getRVertPtr(mesh->faces[i].v[vx2])); + fwrite(&n.x, sizeof(float), 1, expStream); + fwrite(&n.y, sizeof(float), 1, expStream); + fwrite(&n.z, sizeof(float), 1, expStream); + + n=GetVertexNormal(mesh, i,mesh->getRVertPtr(mesh->faces[i].v[vx3])); + fwrite(&n.x, sizeof(float), 1, expStream); + fwrite(&n.y, sizeof(float), 1, expStream); + fwrite(&n.z, sizeof(float), 1, expStream); + } + } + } +// tris[result].VertNormals[s] = Normalize(transpose * (m * get_vertex_normal(&triobj->mesh, j, triobj->mesh.getRVertPtr(fces[j].v[s])))); + if (mesh->getNumVertCol()) + { + Point3 vc; + WriteChunkHdr( (char*)VCOL_ID, 0 ); + fwrite( &nCurObj, sizeof(Uint32), 1, expStream ); + fwrite( &m , sizeof(Uint32), 1, expStream ); + int nbFaces = MeshSet[m].nbFaces; + fwrite( &nbFaces, sizeof(Uint32), 1, expStream ); + for (i=0; igetNumFaces(); i++) + { + matid = ((unsigned long)mesh->getFaceMtlIndex(i)) % MtlCount; + + if (matid == MeshSet[m].MatId) + { + TVFace *f = &mesh->vcFace[i]; + int vert = f->t[vx1]; + vc = mesh->vertCol[vert]; + fwrite(&vc.x, sizeof(float), 1, expStream); + fwrite(&vc.y, sizeof(float), 1, expStream); + fwrite(&vc.z, sizeof(float), 1, expStream); + + vert = f->t[vx2]; + vc = mesh->vertCol[vert]; + fwrite(&vc.x, sizeof(float), 1, expStream); + fwrite(&vc.y, sizeof(float), 1, expStream); + fwrite(&vc.z, sizeof(float), 1, expStream); + + vert = f->t[vx3]; + vc = mesh->vertCol[vert]; + fwrite(&vc.x, sizeof(float), 1, expStream); + fwrite(&vc.y, sizeof(float), 1, expStream); + fwrite(&vc.z, sizeof(float), 1, expStream); + } + } + } + + } + + + // EXPORT MESHSET INFO + WriteChunkHdr( (char*)MESH_ID, 0 ); + fwrite( &nCurObj, sizeof(long), 1, expStream ); + fwrite( &nbMeshs, sizeof(long), 1, expStream ); + + int NodeMatRef = mtlList.GetMtlID(nodeMtl); + for (m=0;mvertCol) + TempStore = m; + fwrite(&TempStore, sizeof(Uint32), 1, expStream); // vertex col list + TempStore = -1; + if (obj->HasUVW()) + TempStore = m; + fwrite(&TempStore, sizeof(Uint32), 1, expStream); // texture list + } +} + + +/*---------------------------------------------------------------------- + ---------------------------------------------------------------------- */ + +BOOL AsciiExp::CheckForAndExportFaceMap(Mtl* mtl, Mesh* mesh, int meshno) +{ + MtlID matid; + ULONG matreq; + + return FALSE; + + if (!mtl || !mesh) return FALSE; + + matreq = mtl->Requirements(-1); + + // ARE WE USING FACE MAPPING? + if (!(matreq & MTLREQ_FACEMAP)) return FALSE; + + + // OK, WE HAVE A FACEMAP SITUATION HERE... + + WriteChunkHdr( (char*)UV_ID, 0 ); + fwrite( &nCurObj, sizeof(Uint16), 1, expStream ); + fwrite( &meshno, sizeof(Uint16), 1, expStream ); + Uint32 mnf = MeshSet[meshno].nbFaces; + fwrite( &mnf, sizeof(Uint32), 1, expStream ); + + for (int i=0; igetNumFaces(); i++) + { + matid = mesh->getFaceMtlIndex(i);//faces[i].getMatID(); + + if (matid == MeshSet[meshno].MatId) + { + Point3 tv[3]; + Face * f; +// NJS_COLOR uv; + + f = &mesh->faces[i]; + make_face_uv( f, tv ); + + fwrite(&tv[0].x, sizeof(float), 1, expStream); + fwrite(&tv[0].y, sizeof(float), 1, expStream); + fwrite(&tv[1].x, sizeof(float), 1, expStream); + fwrite(&tv[1].y, sizeof(float), 1, expStream); + fwrite(&tv[2].x, sizeof(float), 1, expStream); + fwrite(&tv[2].y, sizeof(float), 1, expStream); + } + } + + return TRUE; +} + + +/*---------------------------------------------------------------------- + ---------------------------------------------------------------------- */ + +void AsciiExp::ExportPoints(INode * node, Mesh * mesh) +{ + Matrix3 tm = node->GetObjTMAfterWSM( ip->GetAnimRange().Start() ); + Point3 Trans; + int nbVerts; + + radius = 0.f; + nbVerts = mesh->getNumVerts(); + + // EXPORT MODEL POINTS + WriteChunkHdr( (char*)POINTS_ID, 0 ); + fwrite( &nCurObj, sizeof(Uint32), 1, expStream ); + fwrite( &nbVerts, sizeof(Uint32), 1, expStream ); + + for (int i=0; iGetObjTMAfterWSM( ip->GetAnimRange().Start() ); + v1 = mesh->verts[i]; + //v2 = VectorTransform( tm, v1 ); + //v2 += tm.GetTrans(); + v2 = tm * v1; + Trans = v2; + + //Trans.y = Trans.y; + fwrite( &Trans.x, sizeof(float), 1, expStream ); + fwrite( &Trans.z, sizeof(float), 1, expStream ); + fwrite( &Trans.y, sizeof(float), 1, expStream ); + + if (radius < fabs(Trans.x)) radius = (float)fabs(Trans.x); + if (radius < fabs(Trans.y)) radius = (float)fabs(Trans.y); + if (radius < fabs(Trans.z)) radius = (float)fabs(Trans.z); + } +} + +void AsciiExp::ExportVectors(INode * node, Mesh * mesh) +{ + Matrix3 tm = node->GetObjTMAfterWSM( ip->GetAnimRange().Start() ); + Point3 Trans; + int nbVerts; + + radius = 0.f; + nbVerts = mesh->getNumVerts(); + + // EXPORT MODEL POINTS + WriteChunkHdr( (char*)VECT_ID, 0 ); + fwrite( &nCurObj, sizeof(Uint32), 1, expStream ); + fwrite( &nbVerts, sizeof(Uint32), 1, expStream ); + + for (int i=0; iverts[i]; + + fwrite( &v1.x, sizeof(float), 1, expStream ); + fwrite( &v1.z, sizeof(float), 1, expStream ); + fwrite( &v1.y, sizeof(float), 1, expStream ); + } +} + +/*---------------------------------------------------------------------- + ---------------------------------------------------------------------- */ + +void AsciiExp::ExportNormals(INode * node, Mesh * mesh) +{ + int nbVerts; + Point3 * vnorms; + Point3 fn; // Face normal + Point3 vn; // Vertex normal + + int i; + Matrix3 tm = node->GetObjTMAfterWSM( ip->GetAnimRange().End() ); + BOOL negScale = TMNegParity(tm); + int vx1, vx2, vx3; + + + if (negScale) + { + vx1 = 2; + vx2 = 1; + vx3 = 0; + } + else + { + vx1 = 0; + vx2 = 1; + vx3 = 2; + } + + + mesh->buildNormals(); + + nbVerts = mesh->getNumVerts(); + vnorms = new Point3[nbVerts]; + + for (i=0; igetNumVerts(); i++) + { + vnorms[i] = GetAverageVertNormal(node, mesh, i); + } + + + // EXPORT MODEL NORMALS + WriteChunkHdr( (char*)NORMAL_ID, 0 ); + fwrite( &nCurObj, sizeof(Uint32), 1, expStream ); + fwrite( &nbVerts, sizeof(Uint32), 1, expStream ); + + for (i=0; ifaces[faceNo]; + DWORD smGroup = f->smGroup; + int numNormals; + Point3 vertexNormal; + + // Is normal specified + // SPCIFIED is not currently used, but may be used in future versions. + if (rv->rFlags & SPECIFIED_NORMAL) + { +// fprintf( pStream, "SPECIFIED_NORMAL\n"); + vertexNormal = rv->rn.getNormal(); + } + // If normal is not specified it's only available if the face belongs + // to a smoothing group + else if ((numNormals = rv->rFlags & NORCT_MASK) && smGroup) + { + // If there is only one vertex is found in the rn member. + if (numNormals == 1) + { +// fprintf( pStream, "SINGLE SMOOTHING GROUP\n"); + vertexNormal = rv->rn.getNormal(); + } + else + { +// fprintf( pStream, "SMOOTHING GROUP %d\n", numNormals); + + // If two or more vertices are there you need to step through them + // and find the vertex with the same smoothing group as the current face. + // You will find multiple normals in the ern member. + for (int i = 0; i < numNormals; i++) + { + vertexNormal = rv->ern[i].getNormal(); + } + } + } + else + { +// fprintf( pStream, "NO SMOOTHING GROUP\n"); + // Get the normal from the Face if no smoothing groups are there + vertexNormal = mesh->getFaceNormal(faceNo); + } + + return vertexNormal; +} + +Point3 AsciiExp::GetAverageVertNormal(INode *node, Mesh* mesh, int VertNo) +{ + int i; + int c=0; + Matrix3 tm = node->GetNodeTM(ip->GetAnimRange().Start()); + tm.NoTrans(); + Point3 n= Point3(0.f,0.f,0.f); + for (i=0; igetNumFaces(); i++) { + if (mesh->faces[i].v[0] == VertNo || mesh->faces[i].v[1] == VertNo || mesh->faces[i].v[2] == VertNo) + { + Point3 p0 = tm * mesh->verts[mesh->faces[i].v[0]]; + Point3 p1 = tm * mesh->verts[mesh->faces[i].v[1]]; + Point3 p2 = tm * mesh->verts[mesh->faces[i].v[2]]; + Point3 Nrm = Normalize(CrossProd(p0 - p1, p0 - p2)); + n += Nrm; + c ++; + } + } + n /= (float)c; + return Normalize(n); +} + +void AsciiExp::ExportNodeAnimMesh( INode* node ) +{ + INode *parent; + Matrix3 parentTM, nodeTM, localTM; + parent = node->GetParentNode(); + + TimeValue start = ip->GetAnimRange().Start(); + TimeValue end = ip->GetAnimRange().End(); + int delta = GetTicksPerFrame(); + TimeValue t; + Matrix3 tm; + Matrix3 StartM; + AffineParts ap; + int i; + Quat q; + Quat StartQ, StartU; + Point3 StartP, StartS; + + nodeTM = node->GetNodeTM(start); + parentTM = parent->GetNodeTM(start); + StartM = nodeTM*Inverse(parentTM); + + for (t=start; t<=end; t+=delta) + { + nodeTM = node->GetNodeTM(t); + parentTM = parent->GetNodeTM(t); + tm = nodeTM*Inverse(parentTM); + for (i=0;i<4;i++) { + Point3 m0 = StartM.GetRow(i); + Point3 m1 = tm.GetRow(i); + if (fabs(m0.x-m1.x)>0.001 || + fabs(m0.y-m1.y)>0.001 || + fabs(m0.z-m1.z)>0.001) + break; + + } + if (i!=4) + break; + } + if (t==end) + return; + + char text[250]; + sprintf(text,"%s", node->GetName()); + + WriteChunkHdr( (char*)ANIM_ID, 0); + fwrite( &CHANNEL_TYPE_NODE, sizeof( Uint32 ), 1, expStream ); + fwrite( &nCurObj, sizeof( Uint32 ), 1, expStream ); + fwrite( &totFrames, sizeof( Uint32 ), 1, expStream ); + for (t=start; t<=end; t+=delta) { + // TRANSLATION + tm = node->GetNodeTM(t) * Inverse(node->GetParentTM(t)); + decomp_affine(tm, &ap); + + fwrite( &ap.t.x, sizeof(float), 1, expStream ); // WRITE BONE X-POS + fwrite( &ap.t.z, sizeof(float), 1, expStream ); // WRITE BONE Y-POS + fwrite( &ap.t.y, sizeof(float), 1, expStream ); // WRITE BONE Z-POS + + fwrite( &ap.q.x, sizeof(float), 1, expStream ); // WRITE BONE X-POS + fwrite( &ap.q.z, sizeof(float), 1, expStream ); // WRITE BONE Y-POS + fwrite( &ap.q.y, sizeof(float), 1, expStream ); // WRITE BONE Z-POS + fwrite( &ap.q.w, sizeof(float), 1, expStream ); // WRITE BONE Z-POS + + fwrite( &ap.k.x, sizeof(float), 1, expStream ); // WRITE BONE X-POS + fwrite( &ap.k.z, sizeof(float), 1, expStream ); // WRITE BONE Y-POS + fwrite( &ap.k.y, sizeof(float), 1, expStream ); // WRITE BONE Z-POS + + fwrite( &ap.u.x, sizeof(float), 1, expStream ); // WRITE BONE X-POS + fwrite( &ap.u.z, sizeof(float), 1, expStream ); // WRITE BONE Y-POS + fwrite( &ap.u.y, sizeof(float), 1, expStream ); // WRITE BONE Z-POS + fwrite( &ap.u.w, sizeof(float), 1, expStream ); // WRITE BONE Z-POS + } +} + +void AsciiExp::ExportVisTrack( INode* node ) +{ + Control *VisControl = node->GetVisController(); + if (VisControl) { + float VisF; + TimeValue start = ip->GetAnimRange().Start(); + TimeValue end = ip->GetAnimRange().End(); + int delta = GetTicksPerFrame(); + TimeValue t; + WriteChunkHdr( (char*)VIS_ID, 0); + fwrite( &nCurObj, sizeof( Uint32 ), 1, expStream ); + fwrite( &totFrames, sizeof( Uint32 ), 1, expStream ); + for (t=start; t<=end; t+=delta) { + Interval I = Interval(t, t); + VisControl->GetValue(t, &VisF, I, CTRL_ABSOLUTE); + fwrite( &VisF, sizeof(float), 1, expStream); + } + } + +} + +void AsciiExp::ExportAnimMesh( INode* node ) +{ + TimeValue start = ip->GetAnimRange().Start(); + TimeValue end = ip->GetAnimRange().End(); + int delta = GetTicksPerFrame(); + BOOL needDel; + Mesh *mesh; + TriObject *tri; + TimeValue t; + + WriteChunkHdr( (char*)ANIM_ID, 0); + fwrite( &CHANNEL_TYPE_VERTEX, sizeof( Uint32 ), 1, expStream ); + fwrite( &nCurObj, sizeof( Uint32 ), 1, expStream ); + fwrite( &totFrames, sizeof( Uint32 ), 1, expStream ); + for (t=start; t<=end; t+=delta) + { + tri = GetTriObjectFromNode(node, t, needDel); + if (!tri) return; + mesh = &tri->mesh; + Matrix3 tm = node->GetObjTMAfterWSM( t ); + for (int i=0; igetNumVerts(); i++) + { + Point3 v1; + Point3 v2; + + v1 = mesh->verts[i]; + v2 = tm * v1; + + fwrite( &v2.x, sizeof(float), 1, expStream ); + fwrite( &v2.z, sizeof(float), 1, expStream ); + fwrite( &v2.y, sizeof(float), 1, expStream ); + } + if (needDel) tri->DeleteThis(); + } +} + +/*=========================================================================== + end */ diff --git a/Utils/GinExp/ExpTree.cpp b/Utils/GinExp/ExpTree.cpp new file mode 100644 index 000000000..b1bea22b8 --- /dev/null +++ b/Utils/GinExp/ExpTree.cpp @@ -0,0 +1,536 @@ +#include "AsciiExp.h" + +Matrix3 Uniform_Matrix(Matrix3 orig_cur_mat) +{ + AffineParts parts; + Matrix3 mat; + + ///Remove scaling from orig_cur_mat + //1) Decompose original and get decomposition info + decomp_affine(orig_cur_mat, &parts); + + //2) construct 3x3 rotation from quaternion parts.q + parts.q.MakeMatrix(mat); + + //3) construct position row from translation parts.t + mat.SetRow(3, parts.t); + + return(mat); +} + + //GET_RELATIVE_MATRIX: RETURNS RELATIVE + //MATRIX WITHOUT NON_UNIFORM SCALING +Matrix3 Get_Relative_Matrix(INode *node, int t) +{ + /* Note: This function removes the non-uniform scaling + from MAX node transformations. before multiplying the + current node by the inverse of its parent. The + removal must be done on both nodes before the + multiplication and Inverse are applied. This is especially + useful for Biped export (which uses non-uniform scaling on + its body parts.) */ + + INode *p_node = node->GetParentNode(); + + Matrix3 orig_cur_mat; // for current and parent + Matrix3 orig_par_mat; // original matrices + + Matrix3 cur_mat; // for current and parent + Matrix3 par_mat; // decomposed matrices + + //Get transformation matrices + orig_cur_mat = node->GetNodeTM(t); + if (p_node) + { + orig_par_mat = p_node->GetNodeTM(t); + } else + { + orig_par_mat.IdentityMatrix(); + } + + char Text[256]; + sprintf(Text, "%s", node->GetName()); + cur_mat = orig_cur_mat; + par_mat = orig_par_mat; +/* Control *c = node->GetTMController(); + if (c) + { + if ((c->ClassID() == BIPSLAVE_CONTROL_CLASS_ID) || + (c->ClassID() == BIPBODY_CONTROL_CLASS_ID) || + (c->ClassID() == FOOTPRINT_CLASS_ID)) + { + //Decompose each matrix + cur_mat = Uniform_Matrix(orig_cur_mat); + } + } + if (p_node) + { + c = p_node->GetTMController(); + if (c) + { + if ((c->ClassID() == BIPSLAVE_CONTROL_CLASS_ID) || + (c->ClassID() == BIPBODY_CONTROL_CLASS_ID) || + (c->ClassID() == FOOTPRINT_CLASS_ID)) + { + par_mat = Uniform_Matrix(orig_par_mat); + } + } + }*/ + + //then return relative matrix in coordinate space of parent + return(cur_mat * Inverse( par_mat)); +} + + +void AsciiExp::ExportTree() +{ + WriteChunkHdr( (char*)TREE_ID, 0); + ExportTreeNodes(ip->GetRootNode()); + + WriteChunkHdr( (char*)ANIM_ID, 0); + fwrite( &CHANNEL_TYPE_TREE, sizeof( Uint32 ), 1, expStream ); +// Change by Dave - Need frame count!! + fwrite( &totFrames, sizeof( Uint32 ), 1, expStream ); + ExportTreeAnim(ip->GetRootNode()); +/* + WriteChunkHdr( (char*)MOTION_ID, 0); + fwrite( &totFrames, sizeof( Uint32 ), 1, expStream ); + ExportMotionAnim(ip->GetRootNode()); +*/ + WriteChunkHdr( (char*)KEY_ID, 0); + fwrite( &CHANNEL_TYPE_TREE, sizeof( Uint32 ), 1, expStream ); + fwrite( &CHANNEL_INFO_ALL, sizeof( Uint32) , 1, expStream); + ExportTreeKeyAnim(ip->GetRootNode()); + + ExportParticles(); +} + +void AsciiExp::ExportParticles() +{ + WriteChunkHdr( (char*)PART_ID, 0); + fwrite(&NbEmmitters, sizeof(int), 1, expStream); + NbEmmitters = 0; + ExportParticleNodes(ip->GetRootNode()); +} + +void AsciiExp::ExportParticleNodes( INode *node) +{ + ObjectState os = node->EvalWorldState( ip->GetAnimRange().Start() ); + Object * obj = node->EvalWorldState(ip->GetAnimRange().Start()).obj; + if (obj && obj->GetInterface(I_PARTICLEOBJ)) + { + fwrite(&NbEmmitters, sizeof(int), 1, expStream); + Control *VisControl = node->GetVisController(); + if (VisControl) { + float VisF; + TimeValue start = ip->GetAnimRange().Start(); + TimeValue end = ip->GetAnimRange().End(); + int delta = GetTicksPerFrame(); + TimeValue t; + fwrite( &totFrames, sizeof( Uint32 ), 1, expStream ); + for (t=start; t<=end; t+=delta) { + Interval I = Interval(t, t); + VisControl->GetValue(t, &VisF, I, CTRL_ABSOLUTE); + fwrite( &VisF, sizeof(float), 1, expStream); + } + } else + { + float VisF = 1.f; + TimeValue start = ip->GetAnimRange().Start(); + TimeValue end = ip->GetAnimRange().End(); + int delta = GetTicksPerFrame(); + TimeValue t; + fwrite( &totFrames, sizeof( Uint32 ), 1, expStream ); + for (t=start; t<=end; t+=delta) { + fwrite( &VisF, sizeof(float), 1, expStream); + } + } + } + NbEmmitters ++; + int nbChilds = node->NumberOfChildren(); + fwrite( &nbChilds, sizeof(int), 1, expStream); + for (int c=0;cGetChildNode(c); + if (ChildNode) ExportParticleNodes( ChildNode ); + } +} + +void AsciiExp::ExportTreeNodes( INode * node ) +{ + INode * ChildNode; + long nbChilds; + Matrix3 tm; + AffineParts ap; + + char name[256]; + ObjectState os = node->EvalWorldState( ip->GetAnimRange().Start() ); + Object * obj = node->EvalWorldState(ip->GetAnimRange().Start()).obj; + + if (obj && obj->GetInterface(I_PARTICLEOBJ)) + { + NbEmmitters ++; + } + // EXPORT BONE + sprintf( name, "%s", node->GetName() ); + fprintf( tempStream, "OUTPUTTING - %s\n", name ); // DEBUG FILE + fwrite( &name, sizeof(char), MAX_NAME_LENGTH, expStream ); // WRITE BONE NAME + + // BONE TRANSLATION +/*Dave!! tm = node->GetNodeTM( ip->GetAnimRange().Start() ) * Inverse(node->GetParentTM(ip->GetAnimRange().Start()));*/ + tm = Get_Relative_Matrix(node , ip->GetAnimRange().Start()); + decomp_affine( tm, &ap ); + + fwrite( &ap.t.x, sizeof(float), 1, expStream ); // WRITE BONE X-POS + fwrite( &ap.t.z, sizeof(float), 1, expStream ); // WRITE BONE Y-POS + fwrite( &ap.t.y, sizeof(float), 1, expStream ); // WRITE BONE Z-POS + + fwrite( &ap.q.x, sizeof(float), 1, expStream ); // WRITE BONE ROT X-AXIS + fwrite( &ap.q.z, sizeof(float), 1, expStream ); // WRITE BONE ROT Y-AXIS + fwrite( &ap.q.y, sizeof(float), 1, expStream ); // WRITE BONE ROT Z-AXIS + fwrite( &ap.q.w, sizeof(float), 1, expStream ); // WRITE BONE ROT W ANGLE + + fwrite( &ap.k.x, sizeof(float), 1, expStream); + fwrite( &ap.k.z, sizeof(float), 1, expStream); + fwrite( &ap.k.y, sizeof(float), 1, expStream); + + fwrite( &ap.u.x, sizeof(float), 1, expStream); + fwrite( &ap.u.z, sizeof(float), 1, expStream); + fwrite( &ap.u.y, sizeof(float), 1, expStream); + fwrite( &ap.u.w, sizeof(float), 1, expStream); + + Matrix3 m0(TRUE); + Matrix3 m1(TRUE); + Matrix3 m2(TRUE); + Matrix3 m3(TRUE); + Matrix3 m4(TRUE); + + m0.SetTrans(ap.t); + ap.q.MakeMatrix(m1); + ap.u.MakeMatrix(m2); + m3.Scale(ap.k); + m4 = Inverse(m2); + + Matrix3 j = m0 * m1 * m4 * m3 * m2; + Matrix3 y = m2 * m3 * m4 * m1 * m0; + + // EXPORT CHILDREN + nbChilds = ValidateBoneKids( node ); + fwrite( &nbChilds, sizeof(long), 1, expStream ); // WRITE BONE CHILD COUNT + + nbBones++; + nbChilds = node->NumberOfChildren(); + for (int c=0;cGetChildNode(c); + if (ChildNode) ExportTreeNodes( ChildNode ); + } +} +/* +void AsciiExp::ExportMotionAnim( INode* node) +{ + TimeValue start = ip->GetAnimRange().Start(); + TimeValue end = ip->GetAnimRange().End(); + TimeValue t; + int delta = GetTicksPerFrame(); + Matrix3 tm; + AffineParts ap; + INode * ChildNode; + long nbChilds; + Control *Cnt= node->GetTMController(); + + if (Cnt) + if (Cnt->ClassID() == BIPBODY_CONTROL_CLASS_ID) + for (t=start; t<=end; t+=delta) + { +// tm = node->GetNodeTM(t) * Inverse(node->GetParentTM(t)); + tm = node->GetNodeTM(t) ; + decomp_affine(tm, &ap); + + fwrite( &ap.t.x, sizeof(float), 1, expStream ); // WRITE BONE X-POS + fwrite( &ap.t.z, sizeof(float), 1, expStream ); // WRITE BONE Y-POS + fwrite( &ap.t.y, sizeof(float), 1, expStream ); // WRITE BONE Z-POS + } + + nbChilds = node->NumberOfChildren(); + for (int c=0;cGetChildNode(c); + if (ChildNode) ExportMotionAnim( ChildNode ); + } +} +*/ +void AsciiExp::ExportTreeAnim( INode* node ) +{ + TimeValue start = ip->GetAnimRange().Start(); + TimeValue end = ip->GetAnimRange().End(); + TimeValue t; + int delta = GetTicksPerFrame(); + Matrix3 tm; + AffineParts ap; + Point3 prevPos; + Point3 startScale; + Quat prevQ; + Quat scaleQ; + Quat q; + Quat InvQ; + INode * ChildNode; + ObjectState os = node->EvalWorldState( ip->GetAnimRange().Start() ); + Object * obj = node->EvalWorldState(ip->GetAnimRange().Start()).obj; + long nbChilds; + + char name[256]; + sprintf( name, "%s", node->GetName() ); + + for (t=start; t<=end; t+=delta) + { +// tm = node->GetNodeTM(t) * Inverse(node->GetParentTM(t)); + tm = Get_Relative_Matrix(node , t); + decomp_affine(tm, &ap); + + fwrite( &ap.t.x, sizeof(float), 1, expStream ); // WRITE BONE X-POS + fwrite( &ap.t.z, sizeof(float), 1, expStream ); // WRITE BONE Y-POS + fwrite( &ap.t.y, sizeof(float), 1, expStream ); // WRITE BONE Z-POS + + fwrite( &ap.q.x, sizeof(float), 1, expStream ); // WRITE BONE ROT X-AXIS + fwrite( &ap.q.z, sizeof(float), 1, expStream ); // WRITE BONE ROT Y-AXIS + fwrite( &ap.q.y, sizeof(float), 1, expStream ); // WRITE BONE ROT Z-AXIS + fwrite( &ap.q.w, sizeof(float), 1, expStream ); // WRITE BONE ROT W ANGLE + + fwrite( &ap.k.x, sizeof(float), 1, expStream ); // WRITE BONE X-POS + fwrite( &ap.k.z, sizeof(float), 1, expStream ); // WRITE BONE Y-POS + fwrite( &ap.k.y, sizeof(float), 1, expStream ); // WRITE BONE Z-POS + + fwrite( &ap.u.x, sizeof(float), 1, expStream ); // WRITE BONE ROT X-AXIS + fwrite( &ap.u.z, sizeof(float), 1, expStream ); // WRITE BONE ROT Y-AXIS + fwrite( &ap.u.y, sizeof(float), 1, expStream ); // WRITE BONE ROT Z-AXIS + fwrite( &ap.u.w, sizeof(float), 1, expStream ); // WRITE BONE ROT W ANGLE + } + + nbChilds = node->NumberOfChildren(); + fwrite( &nbChilds, sizeof(int), 1, expStream); + for (int c=0;cGetChildNode(c); + if (ChildNode) ExportTreeAnim( ChildNode ); + } +} + +void AsciiExp::ExportTreeKeyAnim( INode* node) +{ + TimeValue start = ip->GetAnimRange().Start(); + TimeValue end = ip->GetAnimRange().End(); + int delta = GetTicksPerFrame(); + Matrix3 tm; + AffineParts ap, StartAp; + Point3 prevPos; + Point3 startScale; + Quat prevQ; + Quat scaleQ; + Quat q; + Quat InvQ; + INode * ChildNode; + ObjectState os = node->EvalWorldState( ip->GetAnimRange().Start() ); + Object * obj = node->EvalWorldState(ip->GetAnimRange().Start()).obj; + long nbChilds; + + int num; + int i; + int FrameN; + Interval ivalid; + IKey k; + Control *c; + IKeyControl *ikeys; + ITCBPoint3Key tcbPosKey; + ITCBRotKey tcbRotKey; + ITCBScaleKey tcbSclKey; + IBezPoint3Key bezPosKey; + IBezQuatKey bezRotKey; + IBezScaleKey bezSclKey; + ILinPoint3Key linPosKey; + ILinRotKey linRotKey; + ILinScaleKey linSclKey; + TimeValue time; + + + TSTR Name = node->GetName(); + TimeList tl; + tl.Clear(); + INode *KeyNode = node; + c = KeyNode->GetTMController(); + if (c) { + + if ((c->ClassID() == BIPSLAVE_CONTROL_CLASS_ID) || + (c->ClassID() == BIPBODY_CONTROL_CLASS_ID) || + (c->ClassID() == FOOTPRINT_CLASS_ID)) + { + for (i=0; iNumSubs(); i++) + { + Animatable *a = c->SubAnim(i); + int k = a->NumKeys(); + for (int j=0; jGetKeyTime(j); + if (time > start && time < end) + tl.Add( time ); + } + } + int k = c->NumKeys(); + for (int j=0; jGetKeyTime(j); + if (time > start && time < end) + tl.Add( time ); + } + + } + + c = c->GetPositionController(); + if (c) { + ikeys = GetKeyControlInterface(c); + if (ikeys) { + + num = ikeys->GetNumKeys(); + if (num >0) + { + for (i = 0; iClassID() == Class_ID(TCBINTERP_POSITION_CLASS_ID, 0)) { + ikeys->GetKey(i, &tcbPosKey); + time = tcbPosKey.time; + } else + if (c->ClassID() == Class_ID(HYBRIDINTERP_POSITION_CLASS_ID, 0)) { + ikeys->GetKey(i, &bezPosKey); + time = bezPosKey.time; + } else + if (c->ClassID() == Class_ID(LININTERP_POSITION_CLASS_ID, 0)) { + ikeys->GetKey(i, &linPosKey); + time = linPosKey.time; + } + if (time > start && time < end) + tl.Add(time); + } + } + } + } + } + c = KeyNode->GetTMController(); + if (c) { + c = c->GetRotationController(); + if (c) { + ikeys = GetKeyControlInterface(c); + if (ikeys) { + + num = ikeys->GetNumKeys(); + if (num >0) + { + for (i = 0; iClassID() == Class_ID(TCBINTERP_ROTATION_CLASS_ID, 0)) { + ikeys->GetKey(i, &tcbRotKey); + int rots; + if (i) + { + ITCBRotKey oldtcbRotKey; + ikeys->GetKey(i-1, &oldtcbRotKey); + rots = fabs(tcbRotKey.val.angle - oldtcbRotKey.val.angle) / (0.6667 * PI) + 1; + for (int k=1; kClassID() == Class_ID(HYBRIDINTERP_ROTATION_CLASS_ID, 0)) { + ikeys->GetKey(i, &bezRotKey); + time = bezRotKey.time; + } else + if (c->ClassID() == Class_ID(LININTERP_ROTATION_CLASS_ID, 0)) { + ikeys->GetKey(i, &linRotKey); + time = linRotKey.time; + } + if (time > start && time < end) + tl.Add(time); + } + } + } + } + } + c = KeyNode->GetTMController(); + if (c) { + c = c->GetScaleController(); + if (c) { + ikeys = GetKeyControlInterface(c); + if (ikeys) { + + num = ikeys->GetNumKeys(); + if (num >0) + { + for (i = 0; iClassID() == Class_ID(TCBINTERP_SCALE_CLASS_ID, 0)) { + ikeys->GetKey(i, &tcbSclKey); + time = tcbSclKey.time; + } else + if (c->ClassID() == Class_ID(HYBRIDINTERP_SCALE_CLASS_ID, 0)) { + ikeys->GetKey(i, &bezSclKey); + time = bezSclKey.time; + } else + if (c->ClassID() == Class_ID(LININTERP_SCALE_CLASS_ID, 0)) { + ikeys->GetKey(i, &linSclKey); + time = linSclKey.time; + } + + if (time > start && time < end) + tl.Add(time); + } + } + } + } + } + if (tl.Count()) + { + tl.Add(start); + tl.Add(end); + } + num = tl.Count(); + fwrite( &num, sizeof( Uint32 ), 1, expStream ); + + for (num = 0; num GetNodeTM(time) * Inverse(node->GetParentTM(time)); + tm = Get_Relative_Matrix(node , time); + + FrameN = (int)((float)(time - start) / delta); + + decomp_affine(tm, &ap); + if (num == 0) + StartAp = ap; + + fwrite( &FrameN, sizeof(Uint32), 1, expStream); + fwrite( &ap.t.x, sizeof(float), 1, expStream ); // WRITE BONE X-POS + fwrite( &ap.t.z, sizeof(float), 1, expStream ); // WRITE BONE Y-POS + fwrite( &ap.t.y, sizeof(float), 1, expStream ); // WRITE BONE Z-POS + + fwrite( &ap.q.x, sizeof(float), 1, expStream ); // WRITE BONE X-POS + fwrite( &ap.q.z, sizeof(float), 1, expStream ); // WRITE BONE Y-POS + fwrite( &ap.q.y, sizeof(float), 1, expStream ); // WRITE BONE Z-POS + fwrite( &ap.q.w, sizeof(float), 1, expStream ); // WRITE BONE Z-POS + + fwrite( &ap.k.x, sizeof(float), 1, expStream ); // WRITE BONE X-POS + fwrite( &ap.k.z, sizeof(float), 1, expStream ); // WRITE BONE Y-POS + fwrite( &ap.k.y, sizeof(float), 1, expStream ); // WRITE BONE Z-POS + + fwrite( &ap.u.x, sizeof(float), 1, expStream ); // WRITE BONE X-POS + fwrite( &ap.u.z, sizeof(float), 1, expStream ); // WRITE BONE Y-POS + fwrite( &ap.u.y, sizeof(float), 1, expStream ); // WRITE BONE Z-POS + fwrite( &ap.u.w, sizeof(float), 1, expStream ); // WRITE BONE Z-POS + } + + CurBone++; + nbChilds = node->NumberOfChildren(); + fwrite( &nbChilds, sizeof(int), 1, expStream); + for (int cn=0;cnGetChildNode(cn); + if (ChildNode) ExportTreeKeyAnim( ChildNode); + } +} + diff --git a/Utils/GinExp/ExpUProp.cpp b/Utils/GinExp/ExpUProp.cpp new file mode 100644 index 000000000..720f420c5 --- /dev/null +++ b/Utils/GinExp/ExpUProp.cpp @@ -0,0 +1,96 @@ +/*========================================================================= + + EXPUPROP.CPP + + Author: Dave + Created: + Project: + Purpose: + + Copyright (c) 1998 Climax Development Ltd + +===========================================================================*/ + +/*---------------------------------------------------------------------- + Includes + -------- */ + +#include "AsciiExp.h" + + +/* Std Lib + ------- */ + +/* Glib + ---- */ + +/* Local + ----- */ + +/* Graphics + -------- */ + +/*---------------------------------------------------------------------- + Tyepdefs && Defines + ------------------- */ + +/*---------------------------------------------------------------------- + Structure defintions + -------------------- */ + +/*---------------------------------------------------------------------- + Positional Vars + --------------- */ + +/*---------------------------------------------------------------------- + Function Prototypes + ------------------- */ + +/*---------------------------------------------------------------------- + Vars + ---- */ + +/*---------------------------------------------------------------------- + Data + ---- */ + +/*---------------------------------------------------------------------- + Function: + Purpose: + Params: + Returns: + ---------------------------------------------------------------------- */ +const char UPROP_ID[] = MAKE_LONG( 'P', 'R', 'O', 'P' ); + +/*---------------------------------------------------------------------- + Function: + Purpose: + Params: + Returns: + ---------------------------------------------------------------------- */ +void AsciiExp::ExportProp( INode* node ) +{ +ObjectState os; +Object * obj; +TSTR UPBuffer; +Uint32 Len; +Uint32 ModNum=nCurNode; +char *TxtPtr; + + os = node->EvalWorldState( ip->GetAnimRange().Start() ); + obj = node->EvalWorldState( ip->GetAnimRange().Start() ).obj; + + if (!os.obj) return; +// if (!obj->IsRenderable()) return; + if (node->IsNodeHidden()) return; +// if (!obj || os.obj->SuperClassID() != GEOMOBJECT_CLASS_ID) return; // Safety net. This shouldn't happen. + + node->GetUserPropBuffer(UPBuffer); + Len=UPBuffer.Length();//strlen(UPBuffer); + Len=(Len+3)&0xfffc; + TxtPtr=UPBuffer.data(); + WriteChunkHdr( (char*)UPROP_ID, 0 ); + fwrite( &ModNum, sizeof(Uint32), 1, expStream ); + fwrite( &Len, sizeof(Uint32), 1, expStream ); + fwrite( TxtPtr, sizeof(char), Len, expStream ); +} diff --git a/Utils/GinExp/Triobjed.h b/Utils/GinExp/Triobjed.h new file mode 100644 index 000000000..a1702308d --- /dev/null +++ b/Utils/GinExp/Triobjed.h @@ -0,0 +1,958 @@ +/********************************************************************** + *< + FILE: triobjed.h + + DESCRIPTION: Editable Triangle Mesh Object + + CREATED BY: Rolf Berteig + + HISTORY: created 4 March 1996 + + *> Copyright (c) 1996, All Rights Reserved. + **********************************************************************/ + +#ifndef __TRIOBJED__ +#define __TRIOBJED__ + +#include "meshadj.h" +typedef Tab Point3Tab; + +#ifdef _SUBMTLASSIGNMENT + #include "sbmtlapi.h" +#endif + +// Selection levels +#define SL_OBJECT 0 +#define SL_VERTEX 1 +#define SL_FACE 2 +#define SL_EDGE 3 + +#define ALIGN_CONST 0 +#define ALIGN_VIEW 1 + +#define DEF_PICKBOX_SIZE 4 + +class WeldVertCMode; +class CreateVertCMode; +class BuildFaceCMode; +class DivideEdgeCMode; +class TurnEdgeCMode; +class AttachPickMode; +class ExtrudeCMode; +class AlignFaceCMode; +class FlipNormCMode; +class CutEdgeCMode; // SCA + +#define CID_CREATEVERT CID_USER + 973 +#define CID_BUILDFACE CID_USER + 975 +#define CID_WELDVERT CID_USER + 978 +#define CID_DIVIDEEDGE CID_USER + 979 +#define CID_TURNEDGE CID_USER + 980 +#define CID_EXTRUDE CID_USER + 990 +#define CID_ALIGNFACE CID_USER + 1000 +#define CID_FLIPNORM CID_USER + 1010 +#define CID_CUTEDGE CID_USER + 1020 // SCA + +#define SELTYPE_SINGLE 1 +#define SELTYPE_POLY 2 +#define SELTYPE_ELEMENT 3 + +#define IDC_SELSINGLE 0x4010 +#define IDC_SELPOLY 0x4011 +#define IDC_SELELEMENT 0x4012 + +#define MAX_MATID 0xffff + +class NamedSelSetList { + public: + Tab sets; + Tab names; + + ~NamedSelSetList(); + BitArray &operator[](int i) {return *sets[i];} + int Count() {return sets.Count();} + void AppendSet(BitArray &nset,TSTR &name); + void DeleteSet(int i); + IOResult Load(ILoad *iload); + IOResult Save(ISave *isave); + void SetSize(int size); + NamedSelSetList& operator=(NamedSelSetList& from); + void DeleteSetElements(BitArray &set,int m=1); + int FindSet(TSTR &name); + }; + +#ifdef _SUBMTLASSIGNMENT +class EditTriObject : public TriObject, public EventUser, public ISubMtlAPI { +#else +class EditTriObject : public TriObject, public EventUser { +#endif + public: + + // Class vars + static HWND hParams1; + static HWND hParams2; + static HACCEL hAccel; + static Interface *ip; + static MoveModBoxCMode *moveMode; + static RotateModBoxCMode *rotMode; + static UScaleModBoxCMode *uscaleMode; + static NUScaleModBoxCMode *nuscaleMode; + static SquashModBoxCMode *squashMode; + static SelectModBoxCMode *selectMode; + static WeldVertCMode *weldVertMode; + static CreateVertCMode *createVertMode; + static BuildFaceCMode* buildFaceMode; + static TurnEdgeCMode* turnEdgeMode; + static DivideEdgeCMode* divideEdgeMode; + static AttachPickMode* attachPickMode; + static ExtrudeCMode* extrudeMode; + static AlignFaceCMode* alignFaceMode; + static FlipNormCMode* flipMode; + static CutEdgeCMode * cutEdgeMode; // SCA + static float falloff, pinch, bubble; + static int selType; + static float normScale; + static BOOL selByVert; + static BOOL inBuildFace; + static BOOL faceUIValid; + static BOOL inNormalMove; + static BOOL ignoreBackfaces, ignoreBackedges; + static BOOL ignoreVisEdge; + static int pickBoxSize; + static int weldBoxSize; + static int attachMat; + static BOOL condenseMat; + static bool sliceSplit, cutQuadSafe, cutRefine; + static Quat sliceRot; + static Point3 sliceCenter; + static float sliceSize; + static bool sliceMode; + + // Cache for computing coord. systems + static FaceClusterList *clust; + static Point3Tab *centers; + static Point3Tab *normals; + static DWORDTab *vclust; + static Tab *distTab; + Point3Tab &CoordCenters(); + Point3Tab &CoordNormals(); + DWORDTab &VertClusters(); + Tab *EdgeDistTab(int iterations); + void FreeDistTab(); + void FreeCoordCaches(); + void MakeClusterList(); + void InvalidateCoordCaches(BOOL selChange=FALSE); + + NamedSelSetList vselSet; + NamedSelSetList fselSet; + NamedSelSetList eselSet; + Tab cont; + + EditTriObject(); + + // Animatable methods + void DeleteThis() {delete this;} + Class_ID ClassID() {return Class_ID(EDITTRIOBJ_CLASS_ID,0);} + void GetClassName(TSTR& s) ;//{s = GetString(IDS_RB_EDITABLEMESH);} + void BeginEditParams(IObjParam *ip, ULONG flags,Animatable *prev); + void EndEditParams(IObjParam *ip, ULONG flags,Animatable *next); + int NumSubs() {return cont.Count();} + Animatable* SubAnim(int i); + TSTR SubAnimName(int i); + BOOL AssignController(Animatable *control,int subAnim); + int SubNumToRefNum(int subNum) {return subNum;} + BOOL SelectSubAnim(int subNum); + + // Reference methods + int NumRefs() {return cont.Count();} + RefTargetHandle GetReference(int i); + void SetReference(int i, RefTargetHandle rtarg); + void CreateContArray(); + void SynchContArray(); + //BOOL BypassTreeView(); + void DeletePointConts(BitArray &set); + void PlugControllersSel(TimeValue t,BitArray &set); + BOOL PlugControl(TimeValue t,int i); + void SetPointAnim(TimeValue t, int i, Point3 pt); + BOOL CloneVertCont(int from, int to); + RefResult NotifyRefChanged(Interval changeInt, RefTargetHandle hTarget, PartID& partID, RefMessage message); + + // BaseObject methods + ObjectState Eval(TimeValue time); + void Transform(TimeValue t, Matrix3& partm, Matrix3 tmAxis, BOOL localOrigin, Matrix3 xfrm, BOOL ar, int type); + void Move(TimeValue t, Matrix3& partm, Matrix3& tmAxis, Point3& val, BOOL localOrigin=FALSE); + void Rotate(TimeValue t, Matrix3& partm, Matrix3& tmAxis, Quat& val, BOOL localOrigin=FALSE); + void Scale(TimeValue t, Matrix3& partm, Matrix3& tmAxis, Point3& val, BOOL localOrigin=FALSE); + void TransformStart(TimeValue t) {if (ip) ip->LockAxisTripods(TRUE);} + void TransformFinish(TimeValue t) {if (ip) ip->LockAxisTripods(FALSE); InvalidateCoordCaches(); FreeDistTab();} + void TransformCancel(TimeValue t) {if (ip) ip->LockAxisTripods(FALSE); FreeDistTab();} + int Display(TimeValue t, INode* inode, ViewExp *vpt, int flags); + void GetWorldBoundBox (TimeValue t, INode * inode, ViewExp* vp, Box3& box); + void GetLocalBoundBox (TimeValue t, INode* inode, ViewExp* vp, Box3& box); + int HitTest(TimeValue t, INode* inode, int type, int crossing, int flags, IPoint2 *p, ViewExp *vpt, ModContext* mc); + void CloneSelSubComponents(TimeValue t); + void AcceptCloneSelSubComponents(TimeValue t); + void SelectSubComponent( + HitRecord *hitRec, BOOL selected, BOOL all, BOOL invert=FALSE); + void ClearSelection(int selLevel); + void SelectAll(int selLevel); + void InvertSelection(int selLevel); + void ActivateSubobjSel(int level, XFormModes& modes ); + void GetSubObjectCenters(SubObjAxisCallback *cb,TimeValue t,INode *node,ModContext *mc); + void GetSubObjectTMs(SubObjAxisCallback *cb,TimeValue t,INode *node,ModContext *mc); + + // Object methods + TCHAR *GetObjectName() ;//{ return GetString(IDS_RB_EDITABLEMESH);} + BOOL IsSubClassOf(Class_ID classID); + + BOOL SupportsNamedSubSels() {return TRUE;} + void ActivateSubSelSet(TSTR &setName); + void NewSetFromCurSel(TSTR &setName); + void RemoveSubSelSet(TSTR &setName); + void SetupNamedSelDropDown(); + int NumNamedSelSets(); + TSTR GetNamedSelSetName(int i); + void SetNamedSelSetName(int i,TSTR &newName); + void NewSetByOperator(TSTR &newName,Tab &sets,int op); + + // Reference methods + RefTargetHandle Clone(RemapDir& remap = NoRemap()); + IOResult Load(ILoad *iload); + IOResult Save(ISave *isave); + + // From EventUser + void Notify() {DeleteSelected();/*delete key was pressed*/} + + // Local methods + void VertsHaveBeenDeleted(BitArray &set); + NamedSelSetList &GetSelSet(); + void KeyboardAccelerator(int id); + void RemoveRollupPages(); + void SetRollupPages(); + int GetSubobjectLevel(); + void SetSubobjectLevel(int level); + void SetSelType(int type); + void HoldVerts(); + void HoldTopology(); + void DeleteSelected(); + Point3 CalcRegionAffect(Point3 v,Point3 center,Point3 delta); + Point3 CalcRegionAffect(float dist,Point3 delta); + void DeleteVertSet(BitArray set); + void DeleteFaceSet(BitArray set, BitArray *isoVert); + void HideSelectedVerts(); + void UnhideAllVerts(); + void HideSelectedFaces(); + void UnhideAllFaces(); + BOOL WeldSelectedVerts(float thresh); + void WeldSelectedVerts(Point3 pt); + void CollapseSelectedVerts(); + void CollapseSelFaces(); + void CollapseSelEdges(); + void WeldVertSet(BitArray set,Point3 *weldPoint=NULL); + void AddNewVertex(Point3 pt); + void BuildNewFace(int *v); + void MakePlanar(); + float GetPolyFaceThesh(); + HitRecord *VertHitsToFaceHits(HitRecord *hitRec,BOOL all); + DWORD GetSelSmoothBits(DWORD &invalid); + void SetSelSmoothBits(DWORD bits,DWORD which); + void SelectBySmoothGroup(DWORD bits,BOOL clear); + void AutoSmooth(float thresh); + DWORD GetUsedSmoothBits(); + int GetSelMatIndex(); + void SetSelMatIndex(int index); + void SelectByMat(int index,BOOL clear); + void InvalidateSurfaceUI(); + void FlipSelNormals(); + void FlipANormal(int ix); + void UnifySelNormals(); + void DetachVertFaces(TSTR &name,BOOL doFaces,BOOL del=TRUE,BOOL elem=FALSE); + void ShowNormals(); + void SetSelEdgeVis(BOOL onOff); + void AutoEdge(float angle); + void AttachObject(INode *node); + void MultiAttachObject(INodeTab &nodeTab); + void TesselateFaces(float tens,BOOL edge); + void ExplodeFaces(float thresh, BOOL objs, TSTR &name); + void DoExtrude(); + void StartExtrudeMode(); + void BeginNormalMove(TimeValue t); + void EndNormalMove(TimeValue t,BOOL accept); + void NormalMove(TimeValue t, float amount); + void CloneSelVerts(); + void CloneSelFaces(BitArray &set); + void BreakVerts(); + void RemoveIsoVerts(); + void AlignVertsTo(int which); + void AlignFacesTo(int which,DWORD f); + Matrix3 ComputeAlignTM(int which, float &zcent); + void SetEdgeVisCheckState(); + void NSCopy(); + void NSPaste(); + BOOL GetUniqueSetName(TSTR &name); + int SelectNamedSet(); + void SelectByNormal(BOOL front); + void SetNumSelLabel(); + Color GetSelVertColor(); + void SetSelVertColor(Color c); + void SelectVertByColor(Point3 selcol,int selDeltaR,int selDeltaG,int selDeltaB,BOOL add,BOOL sub); + BOOL AllocateVertColor(); + BOOL SplitSharedVertCol(); + void CreateCurveFromEdge(TSTR name,int curveType,BOOL ignoreHiddenEdges); + void SelectOpenEdges(); + BOOL DoAttachMatOptionDialog(); + int DoCut (Point3 & viewNorm, DWORD e1, float prop1, DWORD e2, float prop2); // SCA + void Slice (); + void EnterSliceMode (HWND hwnd); + void ExitSliceMode (HWND hwnd); + +#ifdef _SUBMTLASSIGNMENT + void* GetInterface(ULONG id); + MtlID GetNextAvailMtlID(); + BOOL HasFaceSelection(); + void SetSelFaceMtlID(MtlID id, BOOL bResetUnsel = FALSE); + int GetSelFaceUniqueMtlID(); + int GetSelFaceAnyMtlID(); + int GetMaxMtlID(); +#endif + + }; + +// --- Command Modes ----------------------------------------------- + +class ExtrudeMouseProc : public MouseCallBack { + private: + MoveTransformer moveTrans; + EditTriObject *eo; + Interface *ip; + IPoint2 om; + + public: + ExtrudeMouseProc(EditTriObject* o, IObjParam *i) + : moveTrans(i) {eo=o;ip=i;} + int proc( + HWND hwnd, + int msg, + int point, + int flags, + IPoint2 m ); + }; + +class ExtrudeSelectionProcessor : public GenModSelectionProcessor { + protected: + HCURSOR GetTransformCursor(); + + public: + ExtrudeSelectionProcessor(ExtrudeMouseProc *mc, EditTriObject *o, IObjParam *i) + : GenModSelectionProcessor(mc,o,i) {} + }; + +class ExtrudeCMode : public CommandMode { + private: + ChangeFGObject fgProc; + ExtrudeSelectionProcessor mouseProc; + ExtrudeMouseProc eproc; + EditTriObject* eo; + + public: + ExtrudeCMode(EditTriObject* o, IObjParam *i) : + fgProc(o), mouseProc(&eproc,o,i), eproc(o,i) {eo=o;} + + int Class() { return MODIFY_COMMAND; } + int ID() { return CID_EXTRUDE; } + MouseCallBack *MouseProc(int *numPoints) { *numPoints=2; return &mouseProc; } + ChangeForegroundCallback *ChangeFGProc() { return &fgProc; } + BOOL ChangeFG( CommandMode *oldMode ) { return oldMode->ChangeFGProc() != &fgProc; } + void EnterMode(); + void ExitMode(); + }; + + + +class AttachPickMode : + public PickModeCallback, + public PickNodeCallback { + public: + EditTriObject* eo; + IObjParam *ip; + + AttachPickMode(EditTriObject* o, IObjParam *i) + {eo=o;ip=i;} + BOOL HitTest(IObjParam *ip,HWND hWnd,ViewExp *vpt,IPoint2 m,int flags); + BOOL Pick(IObjParam *ip,ViewExp *vpt); + void EnterMode(IObjParam *ip); + void ExitMode(IObjParam *ip); + + BOOL Filter(INode *node); + BOOL RightClick(IObjParam *ip,ViewExp *vpt) {return TRUE;} + PickNodeCallback *GetFilter() {return this;} + }; + + +class WeldVertMouseProc : public MoveModBox { + private: + EditTriObject *et; + IObjParam *ip; + + public: + WeldVertMouseProc(EditTriObject* e, IObjParam *i) + : MoveModBox(e,i) {et=e;ip=i;} + BOOL HitTestVerts(IPoint2 &m, ViewExp *vpt,int &v); + int proc( + HWND hwnd, + int msg, + int point, + int flags, + IPoint2 m ); + }; + +class WeldVertSelectionProcessor : public GenModSelectionProcessor { + protected: + HCURSOR GetTransformCursor(); + + public: + WeldVertSelectionProcessor(WeldVertMouseProc *mc, Object *o, IObjParam *i) + : GenModSelectionProcessor(mc,o,i) {} + }; + +class WeldVertCMode : public CommandMode { + private: + ChangeFGObject fgProc; + WeldVertSelectionProcessor mouseProc; + WeldVertMouseProc eproc; + EditTriObject* et; + + public: + WeldVertCMode(EditTriObject* mod, IObjParam *i) : + fgProc(mod), mouseProc(&eproc,mod,i), eproc(mod,i) {et=mod;} + + int Class() { return MODIFY_COMMAND; } + int ID() { return CID_WELDVERT; } + MouseCallBack *MouseProc(int *numPoints) {*numPoints=2; return &mouseProc;} + ChangeForegroundCallback *ChangeFGProc() {return &fgProc; } + BOOL ChangeFG( CommandMode *oldMode ) {return oldMode->ChangeFGProc() != &fgProc;} + void EnterMode(); + void ExitMode(); + }; + + +class CreateVertMouseProc : public MouseCallBack { + private: + EditTriObject *et; + IObjParam *ip; + + public: + CreateVertMouseProc(EditTriObject* mod, IObjParam *i) + {et=mod;ip=i;} + int proc( + HWND hwnd, + int msg, + int point, + int flags, + IPoint2 m ); + }; + +class CreateVertCMode : public CommandMode { + private: + ChangeFGObject fgProc; + CreateVertMouseProc proc; + EditTriObject* et; + + public: + CreateVertCMode(EditTriObject* mod, IObjParam *i) + : fgProc(mod), proc(mod,i) {et=mod;} + + int Class() { return MODIFY_COMMAND; } + int ID() { return CID_CREATEVERT; } + MouseCallBack *MouseProc(int *numPoints) {*numPoints=1; return &proc;} + ChangeForegroundCallback *ChangeFGProc() {return &fgProc;} + BOOL ChangeFG(CommandMode *oldMode) {return oldMode->ChangeFGProc()!= &fgProc;} + void EnterMode(); + void ExitMode(); + }; + + +class BuildFaceMouseProc : public MouseCallBack { + private: + EditTriObject *et; + IObjParam *ip; + int vts[3]; + IPoint3 mpts[3]; + int pt; + IPoint2 lm; + + public: + BuildFaceMouseProc(EditTriObject* e, IObjParam *i); + void DrawFace(HWND hWnd,IPoint2 &m); + BOOL HitTestVerts(IPoint2 &m, ViewExp *vpt,int &v); + int proc( + HWND hwnd, + int msg, + int point, + int flags, + IPoint2 m ); + }; + +class BuildFaceCMode : public CommandMode { + private: + ChangeFGObject fgProc; + BuildFaceMouseProc proc; + EditTriObject *et; + + public: + BuildFaceCMode(EditTriObject* e, IObjParam *i) + : fgProc(e), proc(e,i) {et=e;} + + int Class() { return MODIFY_COMMAND; } + int ID() { return CID_BUILDFACE; } + MouseCallBack *MouseProc(int *numPoints) {*numPoints=999999; return &proc;} + ChangeForegroundCallback *ChangeFGProc() {return &fgProc;} + BOOL ChangeFG(CommandMode *oldMode) {return oldMode->ChangeFGProc()!= &fgProc;} + void EnterMode(); + void ExitMode(); + }; + + +class AlignFaceMouseProc : public MouseCallBack { + public: + EditTriObject *et; + IObjParam *ip; + int which; + + AlignFaceMouseProc(EditTriObject* e, IObjParam *i) + {et=e;ip=i;which=ALIGN_CONST;} + HitRecord *HitTestFaces(IPoint2 &m, ViewExp *vpt); + int proc( + HWND hwnd, + int msg, + int point, + int flags, + IPoint2 m ); + }; + +class AlignFaceCMode : public CommandMode { + public: + ChangeFGObject fgProc; + AlignFaceMouseProc proc; + EditTriObject* et; + + AlignFaceCMode(EditTriObject* e, IObjParam *i) + : fgProc(e), proc(e,i ) {et=e;} + + int Class() { return MODIFY_COMMAND; } + int ID() { return CID_ALIGNFACE; } + MouseCallBack *MouseProc(int *numPoints) {*numPoints=1; return &proc;} + ChangeForegroundCallback *ChangeFGProc() {return &fgProc;} + BOOL ChangeFG(CommandMode *oldMode) {return oldMode->ChangeFGProc()!= &fgProc;} + void EnterMode(); + void ExitMode(); + }; + + +class FlipNormMouseProc : public MouseCallBack { + public: + EditTriObject *et; + IObjParam *ip; + + FlipNormMouseProc(EditTriObject* e, IObjParam *i) + {et=e;ip=i;} + HitRecord *HitTestFaces(IPoint2 &m, ViewExp *vpt); + int proc( + HWND hwnd, + int msg, + int point, + int flags, + IPoint2 m ); + }; + +class FlipNormCMode : public CommandMode { + public: + ChangeFGObject fgProc; + FlipNormMouseProc proc; + EditTriObject* et; + + FlipNormCMode(EditTriObject* e, IObjParam *i) + : fgProc(e), proc(e,i ) {et=e;} + + int Class() {return MODIFY_COMMAND;} + int ID() {return CID_FLIPNORM;} + MouseCallBack *MouseProc(int *numPoints) {*numPoints=1; return &proc;} + ChangeForegroundCallback *ChangeFGProc() {return &fgProc;} + BOOL ChangeFG(CommandMode *oldMode) {return oldMode->ChangeFGProc()!= &fgProc;} + void EnterMode(); + void ExitMode(); + }; + + + +class PickEdgeMouseProc : public MouseCallBack { + public: + EditTriObject *et; + IObjParam *ip; + + PickEdgeMouseProc(EditTriObject* e, IObjParam *i) + {et=e;ip=i;} + HitRecord *HitTestEdges(IPoint2 &m, ViewExp *vpt); + int proc( + HWND hwnd, + int msg, + int point, + int flags, + IPoint2 m ); + virtual void EdgePick(DWORD edge)=0; + }; + +class DivideEdgeProc : public PickEdgeMouseProc { + public: + DivideEdgeProc(EditTriObject* e, IObjParam *i) : PickEdgeMouseProc(e,i) {} + void EdgePick(DWORD edge); + }; + +class TurnEdgeProc : public PickEdgeMouseProc { + public: + TurnEdgeProc(EditTriObject* e, IObjParam *i) : PickEdgeMouseProc(e,i) {} + void EdgePick(DWORD edge); + }; + +class DivideEdgeCMode : public CommandMode { + private: + ChangeFGObject fgProc; + DivideEdgeProc proc; + EditTriObject* et; + + public: + DivideEdgeCMode(EditTriObject* e, IObjParam *i) + : fgProc(e), proc(e,i) {et=e;} + + int Class() { return MODIFY_COMMAND; } + int ID() { return CID_DIVIDEEDGE; } + MouseCallBack *MouseProc(int *numPoints) {*numPoints=1; return &proc;} + ChangeForegroundCallback *ChangeFGProc() {return &fgProc;} + BOOL ChangeFG(CommandMode *oldMode) {return oldMode->ChangeFGProc()!= &fgProc;} + void EnterMode(); + void ExitMode(); + }; + +class TurnEdgeCMode : public CommandMode { + private: + ChangeFGObject fgProc; + TurnEdgeProc proc; + EditTriObject* et; + + public: + TurnEdgeCMode(EditTriObject* e, IObjParam *i) + : fgProc(e), proc(e,i) {et=e;} + + int Class() { return MODIFY_COMMAND; } + int ID() { return CID_TURNEDGE; } + MouseCallBack *MouseProc(int *numPoints) {*numPoints=1; return &proc;} + ChangeForegroundCallback *ChangeFGProc() {return &fgProc;} + BOOL ChangeFG(CommandMode *oldMode) {return oldMode->ChangeFGProc()!= &fgProc;} + void EnterMode(); + void ExitMode(); + }; + +class CutEdgeProc : public MouseCallBack { // SCA -- derived from DivideEdgeProc. +public: + EditTriObject *et; + IObjParam *ip; + DWORD e1; + bool e1set; + float prop1; + IPoint2 m1, oldm2; + + CutEdgeProc(EditTriObject* e, IObjParam *i) { et=e; ip=i; e1set = FALSE;} + HitRecord *HitTestEdges(IPoint2 &m, ViewExp *vpt); + int proc(HWND hwnd, int msg, int point, int flags, IPoint2 m ); + void DrawCutter (HWND hWnd,IPoint2 &m); +}; + +class CutEdgeCMode : public CommandMode { // SCA +private: + ChangeFGObject fgProc; + CutEdgeProc proc; + EditTriObject* et; + +public: + CutEdgeCMode(EditTriObject* e, IObjParam *i) + : fgProc(e), proc(e,i) {et=e;} + + int Class() { return MODIFY_COMMAND; } + int ID() { return CID_CUTEDGE; } + MouseCallBack *MouseProc(int *numPoints) {*numPoints=20; return &proc;} + ChangeForegroundCallback *ChangeFGProc() {return &fgProc;} + BOOL ChangeFG(CommandMode *oldMode) {return oldMode->ChangeFGProc()!= &fgProc;} + void EnterMode(); + void ExitMode(); +}; + +// --- Restore objects --------------------------------------------- + +class MeshSelRestore : public RestoreObj { + public: + BitArray undo, redo; + EditTriObject *et; + int level; + + MeshSelRestore(EditTriObject *et); + void Restore(int isUndo); + void Redo(); + TSTR Description() {return TSTR(_T("Mesh Sel"));} + }; + +class MeshVertRestore : public RestoreObj { + public: + Tab undo, redo; + EditTriObject *et; + + MeshVertRestore(EditTriObject *et); + void Restore(int isUndo); + void Redo(); + void EndHold() {et->ClearAFlag(A_HELD);} + TSTR Description() {return TSTR(_T("Mesh Vert"));} + }; + +class MeshVertColorRestore : public RestoreObj { + public: + Tab undo, redo; + EditTriObject *et; + + MeshVertColorRestore(EditTriObject *et); + void Restore(int isUndo); + void Redo(); + void EndHold() {et->ClearAFlag(A_HELD);} + TSTR Description() {return TSTR(_T("Mesh Vert Color"));} + }; + +class MeshTopoRestore : public RestoreObj { + public: + EditTriObject *et; + Tab uverts, rverts; + Tab uTverts, rTverts; + Tab ufaces, rfaces; + Tab utvFaces, rtvFaces; + Tab uvertCol, rvertCol; + Tab uvcFaces, rvcFaces; + BitArray uvertSel, ufaceSel, uedgeSel, uvertHide; + BitArray rvertSel, rfaceSel, redgeSel, rvertHide; + BOOL undone; + Tab ucont, rcont; + + MeshTopoRestore(EditTriObject *et); + void Restore(int isUndo); + void Redo(); + void EndHold() {et->ClearAFlag(A_HELD);} + TSTR Description() {return TSTR(_T("Mesh Topo"));} + }; + +class MeshVertHideRestore : public RestoreObj { + public: + BitArray undo, redo; + EditTriObject *et; + + MeshVertHideRestore(EditTriObject *et); + void Restore(int isUndo); + void Redo(); + TSTR Description() {return TSTR(_T("Mesh Vert Hide"));} + }; + +class MeshFaceHideRestore : public RestoreObj { + public: + BitArray undo, redo; + EditTriObject *et; + + MeshFaceHideRestore(EditTriObject *et); + void Restore(int isUndo); + void Redo(); + TSTR Description() {return TSTR(_T("Mesh Face Hide"));} + }; + +class MeshFaceMatRestore : public RestoreObj { + public: + Tab undo, redo; + EditTriObject *et; + + MeshFaceMatRestore(EditTriObject *et); + void Restore(int isUndo); + void Redo(); + void EndHold() {et->ClearAFlag(A_HELD);} + TSTR Description() {return TSTR(_T("Mesh Face Mat"));} + }; + +class MeshFaceSmoothRestore : public RestoreObj { + public: + Tab undo, redo; + EditTriObject *et; + + MeshFaceSmoothRestore(EditTriObject *et); + void Restore(int isUndo); + void Redo(); + void EndHold() {et->ClearAFlag(A_HELD);} + TSTR Description() {return TSTR(_T("Mesh Face Smooth"));} + }; + +class MeshEdgeVisRestore : public RestoreObj { + public: + Tab undo, redo; + EditTriObject *et; + + MeshEdgeVisRestore(EditTriObject *et); + void Restore(int isUndo); + void Redo(); + void EndHold() {et->ClearAFlag(A_HELD);} + TSTR Description() {return TSTR(_T("Mesh Edge Vis"));} + }; + +class FaceIndexRec { + public: + DWORD v[3], flags; + }; +class UVFaceIndexRec { + public: + DWORD v[3]; + }; + + +class MeshFaceIndexRestore : public RestoreObj { + public: + Tab undo, redo; + Tab tundo, tredo; + EditTriObject *et; + + MeshFaceIndexRestore(EditTriObject *et); + void Restore(int isUndo); + void Redo(); + TSTR Description() {return TSTR(_T("Mesh Face Index"));} + }; + +class AppendSetRestore : public RestoreObj { + public: + BitArray set; + TSTR name; + NamedSelSetList *setList; + EditTriObject *et; + + AppendSetRestore(NamedSelSetList *sl,EditTriObject *e) { + setList = sl; et = e; + } + void Restore(int isUndo) { + set = *setList->sets[setList->Count()-1]; + name = *setList->names[setList->Count()-1]; + setList->DeleteSet(setList->Count()-1); + if (et->ip) et->ip->NamedSelSetListChanged(); + } + void Redo() { + setList->AppendSet(set,name); + if (et->ip) et->ip->NamedSelSetListChanged(); + } + + TSTR Description() {return TSTR(_T("Append Set"));} + }; + +class DeleteSetRestore : public RestoreObj { + public: + BitArray set; + TSTR name; + int index; + NamedSelSetList *setList; + EditTriObject *et; + + DeleteSetRestore(int i,NamedSelSetList *sl,EditTriObject *e) { + setList = sl; et = e; index = i; + set = *setList->sets[index]; + name = *setList->names[index]; + } + void Restore(int isUndo) { + BitArray *n = new BitArray(set); + TSTR *nm = new TSTR(name); + setList->sets.Insert(index,1,&n); + setList->names.Insert(index,1,&nm); + if (et->ip) et->ip->NamedSelSetListChanged(); + } + void Redo() { + setList->DeleteSet(index); + if (et->ip) et->ip->NamedSelSetListChanged(); + } + + TSTR Description() {return TSTR(_T("Delete Set"));} + }; + +class SetNameRestore : public RestoreObj { + public: + TSTR undo, redo; + int index; + NamedSelSetList *setList; + EditTriObject *et; + SetNameRestore(int i,NamedSelSetList *sl,EditTriObject *e) { + index = i; setList = sl; et = e; + undo = *setList->names[index]; + } + + void Restore(int isUndo) { + redo = *setList->names[index]; + *setList->names[index] = undo; + if (et->ip) et->ip->NamedSelSetListChanged(); + } + void Redo() { + *setList->names[index] = redo; + if (et->ip) et->ip->NamedSelSetListChanged(); + } + + TSTR Description() {return TSTR(_T("Set Name"));} + }; + + + + +class AttachHitByName : public HitByNameDlgCallback { +public: + EditTriObject *eo; + + AttachHitByName(EditTriObject *e) {eo=e;} + TCHAR *dialogTitle();// {return GetString(IDS_RB_MULTIATTACH);} + TCHAR *buttonText();// {return GetString(IDS_RB_ATTACH);} + int filter(INode *node); + void proc(INodeTab &nodeTab); +}; + + +// --- tridata.cpp functions --------------------------------------- + +float AffectRegionFunction(float dist,float falloff,float pinch,float bubble); +Point3 FaceNormal(Mesh &mesh, DWORD fi); +Point3 FaceCenter(Mesh &mesh, DWORD fi); +BOOL AnyFaceVertSelected(Mesh &mesh, DWORD fi); +Point3 AverageSelVertNormal(Mesh& mesh); +Point3 AverageSelVertCenter(Mesh& mesh); +void BuildAverageNormals(Mesh& mesh, Point3Tab& normals); +void MatrixFromNormal(Point3& normal, Matrix3& mat); +void BitArrayDeleteSet(BitArray &ba,BitArray &set,int m=1); +void MeshDeleteVertices(Mesh &mesh,BitArray& set); +void MeshDeleteFaces(Mesh &mesh,BitArray& set); +void PolyFromFace(Mesh& mesh,AdjFaceList &al,DWORD f,BitArray &set,float thresh,BOOL ignoreVisEdge); +void ElementFromFace(Mesh &mesh, AdjFaceList &al,DWORD f, BitArray &set); +AdjFaceList *BuildAdjFaceList(Mesh &mesh); +int GetEdgeIndex(Face &f,DWORD v0,DWORD v1); +DWORD GetOtherIndex(Face *f,DWORD v0,DWORD v1); +float AngleBetweenFaces(Mesh *mesh,DWORD f0, DWORD f1); +void FaceCenterTessellate(Mesh *mesh); +void EdgeTessellate(Mesh *mesh, AdjEdgeList *ae, AdjFaceList *af, float tens); +BOOL IsEdgeSelected(MEdge edge,Face *faces,BitArray &esel); +BOOL IsFaceSelected(MEdge edge,BitArray &fsel); +BOOL IsEdgeVisible(MEdge edge,Face *faces); +Point3 ButterFlySubdivide(MEdge edg,Mesh *mesh,AdjFaceList *af,float tens); +Point3 EdgeCenter(MEdge edge,Point3 *verts); +void ElemExplodeFaces(Mesh *mesh, float thresh, AdjFaceList *af); +void ObjExplodeFaces(EditTriObject *eo, + INode *node, TSTR &name, Interface *ip, + Mesh *mesh, float thresh, AdjFaceList *af); +void SwapEdgeVerts(Face *f,DWORD& v1,DWORD& v2); +DWORD GetFaceVertIndex(Face &f,DWORD v); +void EdgeTessellate(Mesh *mesh, AdjEdgeList *ae, AdjFaceList *af, float tens); +void ExtrudeFaces(EditTriObject *eo, Mesh& mesh, + BitArray& sel, AdjEdgeList& el, BOOL doFace); +BOOL GetDetachObjectName(HWND hDlg,TSTR &name, BOOL &elem); +void DeselectHiddenFaces(Mesh &mesh); +void SyncSelectionSets(Mesh &mesh); + +#endif //__TRIOBJED__ diff --git a/Utils/GinExp/asciiexp.aps b/Utils/GinExp/asciiexp.aps new file mode 100644 index 0000000000000000000000000000000000000000..53d290ccdaf8dde92bdd1c78f6c98ae7596e3f51 GIT binary patch literal 35636 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 +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=ASCIIEXP - WIN32 RELEASE +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "asciiexp.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "asciiexp.mak" CFG="ASCIIEXP - WIN32 RELEASE" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "asciiexp - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "asciiexp - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName ""$/KatBox/Project/asciiexp", VHBAAAAA" +# PROP Scc_LocalPath "." +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "asciiexp - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir ".\Release" +# PROP BASE Intermediate_Dir ".\Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir ".\Release" +# PROP Intermediate_Dir ".\Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /G6 /MD /W3 /GX /Od /I "..\..\..\include" /I "c:\3dsmax3_1\MaxSDK\INCLUDE" /I "c:\3dsmax3_1\cstudio\docs" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c +# SUBTRACT CPP /YX /Yc /Yu +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 +# ADD LINK32 KERNEL32.LIB USER32.LIB GDI32.LIB WINSPOOL.LIB COMDLG32.LIB ADVAPI32.LIB SHELL32.LIB OLE32.LIB OLEAUT32.LIB UUID.LIB ODBC32.LIB ODBCCP32.LIB COMCTL32.LIB /nologo /base:"0X02C60000" /subsystem:windows /dll /incremental:yes /machine:I386 /out:"C:\3dsmax3_1\plugins\GinExp.dle" + +!ELSEIF "$(CFG)" == "asciiexp - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir ".\Debug" +# PROP BASE Intermediate_Dir ".\Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir ".\Debug" +# PROP Intermediate_Dir ".\Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /G6 /MDd /W3 /Gm /GX /ZI /Od /I "c:\3dsmax3_1\MaxSDK\INCLUDE" /I "c:\3dsmax3_1\cstudio\docs" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c +# SUBTRACT CPP /YX /Yc /Yu +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 +# ADD LINK32 KERNEL32.LIB USER32.LIB GDI32.LIB WINSPOOL.LIB COMDLG32.LIB ADVAPI32.LIB SHELL32.LIB OLE32.LIB OLEAUT32.LIB UUID.LIB ODBC32.LIB ODBCCP32.LIB COMCTL32.LIB /nologo /base:"0X02C60000" /subsystem:windows /dll /incremental:no /debug /machine:I386 /out:"C:\3dsmax3_1\plugins\GinExp.dle" +# SUBTRACT LINK32 /pdb:none + +!ENDIF + +# Begin Target + +# Name "asciiexp - Win32 Release" +# Name "asciiexp - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90" +# Begin Source File + +SOURCE=.\asciiexp.def +# End Source File +# Begin Source File + +SOURCE=.\asciiexp.rc +# End Source File +# Begin Source File + +SOURCE=.\ExpBone.cpp +# End Source File +# Begin Source File + +SOURCE=.\ExpCamLight.cpp +# End Source File +# Begin Source File + +SOURCE=.\ExpFFD.cpp + +!IF "$(CFG)" == "asciiexp - Win32 Release" + +!ELSEIF "$(CFG)" == "asciiexp - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\ExpFileIo.cpp +# End Source File +# Begin Source File + +SOURCE=.\ExpFileIo.h +# End Source File +# Begin Source File + +SOURCE=.\ExpMain.cpp +# End Source File +# Begin Source File + +SOURCE=.\ExpMaterial.cpp +# End Source File +# Begin Source File + +SOURCE=.\ExpModel.cpp +# End Source File +# Begin Source File + +SOURCE=.\ExpTree.cpp +# End Source File +# Begin Source File + +SOURCE=.\ExpUProp.cpp +# End Source File +# Begin Source File + +SOURCE=.\resource.h +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd" +# Begin Source File + +SOURCE=.\asciiexp.h +# End Source File +# Begin Source File + +SOURCE=.\ffd.h +# End Source File +# Begin Source File + +SOURCE=.\Triobjed.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe" +# End Group +# Begin Group "Libraries" + +# PROP Default_Filter "*.lib" +# Begin Source File + +SOURCE=\3dsmax3_1\MaxSDK\LIB\CORE.LIB +# End Source File +# Begin Source File + +SOURCE=\3dsmax3_1\MaxSDK\LIB\GEOM.LIB +# End Source File +# Begin Source File + +SOURCE=\3dsmax3_1\MaxSDK\LIB\MESH.LIB +# End Source File +# Begin Source File + +SOURCE=\3dsmax3_1\Maxsdk\Lib\Bmm.lib +# End Source File +# Begin Source File + +SOURCE=\3dsmax3_1\Maxsdk\lib\maxutil.lib +# End Source File +# End Group +# End Target +# End Project diff --git a/Utils/GinExp/asciiexp.h b/Utils/GinExp/asciiexp.h new file mode 100644 index 000000000..e0b498956 --- /dev/null +++ b/Utils/GinExp/asciiexp.h @@ -0,0 +1,456 @@ +//************************************************************************** +//* Asciiexp.h - Katana File Exporter +//* +//* By Tim Swann +//* Climax Development +//* +//* July 7, 1998 TS Initial coding +//* +//* Class definition +//* +//* Copyright (c) 1998, All Rights Reserved. +//*************************************************************************** + +#ifndef __ASCIIEXP__H +#define __ASCIIEXP__H + +#define MAXCOUNT (256*16) +#include +#include +#include +#include "Resource.h" +#include "Istdplug.h" +#include "Stdmat.h" +#include "Decomp.h" +#include "Shape.h" +#include "Interpik.h" + +//#include "Asciitok.h" + +#include "PhyExp.h" +#include "triobjed.h" +#include "ffd.h" +#include "Bmmlib.h" +#include "bipexp.h" +//#include +//#include +//#include +//#include + +typedef unsigned int Uint32; +typedef unsigned short Uint16; +typedef unsigned char Uint8; +typedef bool Bool; + + +extern ClassDesc * GetAsciiExpDesc(); +extern TCHAR * GetString(int id); +extern HINSTANCE hInstance; +extern DWORD WINAPI fn(LPVOID arg); + + +//#define MAKE_LONG( a,b,c,d ) (a | (b<<8) | (c<<16) | (d<<24)) +#define MAKE_LONG(a,b,c,d) { a, b, c, d, 0} +#define MAX_FILE_CHUNKS (4096*2) + +#define OMNI_LIGHT 0 // Omnidirectional +#define TSPOT_LIGHT 1 // Targeted +#define DIR_LIGHT 2 // Directional +#define FSPOT_LIGHT 3 // Free +#define TDIR_LIGHT 4 // Targeted directional + +#define GIN_FLAG_USE_TEXTURE 1 +#define GIN_FLAG_DOUBLE_SIDE 2 +#define GIN_FLAG_IGNORE_LIGHT 4 +#define GIN_FLAG_USE_ENVTEXTURE 32 + +const Uint32 MAX_NODES = 1000; // MAX NODES IN SCENE +const Uint32 MAX_CHUNKFILE_CHUNK_DEPTH = 20; // MAXIMUM NESTED DEPTH OF CHUNKS IN A CHUNKFILE: +const Uint32 MAX_NAME_LENGTH = 32; // MAXIMUM LENGTH OF A NAME STRING +const Uint32 ID_SIZE = 4; // SIZE OF CHUNK ID + +// ANIMATION CHANNEL DEFINITIONS +const Uint32 CHANNEL_TYPE_NONE = 0; // NO CHANNEL APPLIED +const Uint32 CHANNEL_TYPE_VERTEX = 1; // VERTEX ANIMATION +const Uint32 CHANNEL_TYPE_BONE = 2; // BONE ANIMATION +const Uint32 CHANNEL_TYPE_WEIGHTS = 3; // BONE VERTEX WEIGHT ANIMATION +const Uint32 CHANNEL_TYPE_NORMALS = 4; // NORMAL ANIMATION +const Uint32 CHANNEL_TYPE_FFD = 5; // FFD ANIMATION +const Uint32 CHANNEL_TYPE_NODE = 6; // NODE ANIMATION +const Uint32 CHANNEL_TYPE_TREE = 7; // NODE ANIMATION + + +const Uint32 CHANNEL_INFO_POSITION = 0; // NO CHANNEL APPLIED +const Uint32 CHANNEL_INFO_ROTATION = 1; // VERTEX ANIMATION +const Uint32 CHANNEL_INFO_SCALE = 2; // BONE ANIMATION +const Uint32 CHANNEL_INFO_ALL = 3; // BONE ANIMATION + +// COUNT OF NUMBER OF FLOATS FOR EACH ANIMATION CHANNEL TYPE +const Uint32 ChannelTypeSize[] = { 0, 3, 7, 1, 3 }; + + +const char FILE_ID[] = MAKE_LONG( 'G', 'I', 'N', '4' ); +const char OBJ_ID[] = MAKE_LONG( 'O', 'B', 'J', '4' ); +const char POLY_ID[] = MAKE_LONG( 'P', 'O', 'L', 'Y' ); +const char UV_ID[] = MAKE_LONG( 'M', 'A', 'P', '4' ); +const char MESH_ID[] = MAKE_LONG( 'M', 'E', 'S', 'H' ); +const char POINTS_ID[] = MAKE_LONG( 'P', 'T', 'S', '4' ); +const char NORMAL_ID[] = MAKE_LONG( 'N', 'O', 'R', 'M' ); +const char MODEL_ID[] = MAKE_LONG( 'M', 'O', 'D', '4' ); +const char TEXTURE_ID[] = MAKE_LONG( 'T', 'E', 'X', '4' ); +const char MATERIAL_ID[] = MAKE_LONG( 'M', 'A', 'T', '4' ); +const char CAMERA_ID[] = MAKE_LONG( 'C', 'A', 'M', '4' ); +const char LIGHT_ID[] = MAKE_LONG( 'L', 'G', 'H', 'T' ); +const char BONE_ID[] = MAKE_LONG( 'B', 'O', 'N', 'E' ); +const char ANIM_ID[] = MAKE_LONG( 'A', 'N', 'I', 'M' ); +const char FFD_ID[] = MAKE_LONG( 'F', 'F', 'D', '4' ); +const char VCOL_ID[] = MAKE_LONG( 'V', 'C', 'O', 'L' ); +const char PART_ID[] = MAKE_LONG( 'P', 'A', 'R', 'T' ); +const char VIS_ID[] = MAKE_LONG( 'V', 'I', 'S', '4' ); +const char KEY_ID[] = MAKE_LONG( 'K', 'E', 'Y', '4' ); +const char FACENORMAL_ID[] = MAKE_LONG( 'F', 'N', 'R', 'M' ); +const char OBJNODE_ID[] = MAKE_LONG( 'N', 'O', 'D', 'E' ); +const char ORIG_ID[] = MAKE_LONG( 'O', 'R', 'I', 'G' ); +const char VERSION_ID[] = MAKE_LONG( 'V', 'E', 'R', 'S' ); +const char KID_ID[] = MAKE_LONG( 'K', 'I', 'D', '4' ); +const char TREE_ID[] = MAKE_LONG( 'T', 'R', 'E', 'E' ); +const char VECT_ID[] = MAKE_LONG( 'V', 'E', 'C', 'T' ); +//const char MOTION_ID[] = MAKE_LONG( 'M', 'O', 'V', 'E' ); +const char TERMINATOR_ID[] = {0}; + + + +#define VERSION 131 // Version number * 100 +#define KATEXP_CLASS_ID Class_ID(0x37bf4fd7, 0x29976333) // CLASS_ID + + +const float MPI = 3.1415926535897932384626433932795028841792f; +const float HALF_PI = (MPI / 2); +const float ONE_DEGREE = 0.0174532925199432957692369076848861271344f; +const float INV_ONE_DEGREE = 1/ONE_DEGREE; + +#define DELTA 1e-6 // error tolerance + + +enum {VX, VY, VZ, VW}; // axes + +inline float DEG2RAD (float dval) { return(dval*ONE_DEGREE); } +inline float RAD2DEG (float dval) { return(dval*(INV_ONE_DEGREE)); } + + +#define NJD_PI 3.14159265358979323846 +#define NJM_DEG_RAD(n) ((n) * NJD_PI / 180.0) // deg -> rad +#define NJM_DEG_ANG(n) (((Angle)(((n) * 65536.0) / 360.0)) & 0x0000ffff) // deg -> ang +#define NJM_RAD_ANG(n) (((Angle)(((n) * 65536.0) / (2 * NJD_PI))) & 0xffff) // rad -> ang +#define NJM_RAD_DEG(n) ((n) * 180.0 / NJD_PI) // rad -> deg +#define NJM_ANG_DEG(n) ((n) * 360.0 / 65536.0) // ang -> deg +#define NJM_ANG_RAD(n) ((n) * (2 * NJD_PI) / 65536.0) // ang -> rad + + +const float UV_X = 255.f; +const float UV_Y = -255.f; + +typedef struct +{ + INode *N; + int Id; +} NODE_ID; + +typedef struct +{ + MtlID MatId; + Uint16 nbFaces; +} MESH_SET; + +typedef struct +{ + Color colour; + float alpha; +} COLOURDEF; + +typedef struct +{ + COLOURDEF diffuse; + COLOURDEF specular; + float exponent; + Uint32 tex_flags; + Uint32 attr_flags; +} MATERIAL; + +typedef struct +{ + char name[256]; + MATERIAL material; + Uint16 TexID; + Uint16 SubTex[256]; +} MATLIST; + + + +#define MAX_BONE_NODES 64 + +typedef struct +{ + int nodecount; + int vertno; + char names[MAX_BONE_NODES][256]; + float weights[MAX_BONE_NODES]; + int NodeId[MAX_BONE_NODES]; + Point3 Offsets[MAX_BONE_NODES]; +} WEIGHTS; + + +class MtlKeeper +{ +public: + int MtlCount; + int GetSubMtlCount(Mtl* mtl, bool recurse); + BOOL AddMtl(Mtl* mtl); + int GetMtlID(Mtl* mtl); + int Count(); + Mtl* GetMtl(int id); + + Tab mtlTab; +}; + + +class TimeList { + + TimeValue Value[1000]; + int count; + +public: + + TimeList() { + count = 0; + } + + void Add(TimeValue t) { + int i,j; + for (i = 0 ;it) { + for (j = count; j>i; j--) { + Value[j] = Value[j-1]; + } + Value[i] = t; + count++; + return; + } + } + Value[count] = t; + count++; + } + + void Clear() { + count = 0; + } + + int Count() { + return count; + } + + TimeValue Get(int p) { + return Value[p]; + } +}; + + +class AsciiExp : public SceneExport +{ +public: + AsciiExp(); + ~AsciiExp(); + + // SceneExport methods + int ExtCount(); // Number of extensions supported + const TCHAR * Ext(int n); // Extension #n (i.e. "ASC") + const TCHAR * LongDesc(); // Long ASCII description (i.e. "Ascii Export") + const TCHAR * ShortDesc(); // Short ASCII description (i.e. "Ascii") + const TCHAR * AuthorName(); // ASCII Author name + const TCHAR * CopyrightMessage(); // ASCII Copyright message + const TCHAR * OtherMessage1(); // Other message #1 + const TCHAR * OtherMessage2(); // Other message #2 + unsigned int Version(); // Version number * 100 (i.e. v3.01 = 301) + void ShowAbout(HWND hWnd); // Show DLL's "About..." box +#if MAX_RELEASE == 2500 + int DoExport(const TCHAR *name,ExpInterface *ei,Interface *i, BOOL suppressPrompts=FALSE); // Export file +#else + int DoExport(const TCHAR *name,ExpInterface *ei,Interface *i, BOOL suppressPrompts=FALSE,DWORD options=0); // Export file +#endif + + // Node enumeration + BOOL nodeEnum(INode* node); + void BoneProcess(INode* node, int& nodeCount); + void FfdProcess(INode* node, int& nodeCount); + void PreProcessBone( INode* node ); + void NodePreProcess(INode *node); + + // High level export + void ExportNode(INode * node); + void ExportMaterialList(); + void ExportModel(INode* node); + void ExportAnimMesh(INode* node); + void ExportCamera(INode* node); + void ExportLight(INode* node); + + void ExportTree(); + // Mid level export + void ExportMesh(INode* node); + void ExportBones( INode* node ); + void ExportMaterial(INode* node); + void ExportAnimKeys(INode* node); + void ExportFFD( INode* node , FFDMod *FfdMod, ModContext *mc, Mesh *); + + // Low level export + void ExportBoneKeyAnim( INode* node ); + void ExportBoneKeyAnimPos( INode* node ); + void ExportBoneKeyAnimRot( INode* node ); + void ExportBoneKeyAnimScl( INode* node ); + void ExportBoneKeyAnimPos( INode* node , TimeList tl); + void ExportBoneKeyAnimRot( INode* node , TimeList tl); + void ExportBoneKeyAnimScl( INode* node , TimeList tl); + void ExportNodeKeyAnim( INode *node); + void ExportNodeKeyPos( INode *node); + void ExportNodeKeyRot( INode *node); + void ExportNodeKeyScl( INode *node); + void ExportMeshList(INode * node, Mesh * mesh); + void ExportPoints(INode * node, Mesh * mesh); + void ExportVectors(INode * node, Mesh * mesh); + void ExportNormals(INode * node, Mesh * mesh); + void ExportVertCols(INode * node, Mesh * mesh); + void ExportVisTrack( INode* node ); + void ExportBoneAnim( INode* node ); + void ExportLimb( INode* nodeptr ); + void ExportWeights( INode* nodeptr ); + void ExportNodeAnimMesh( INode* node ); + void ExportMeshVertAnim(INode *node, EditTriObject *e); + void DumpMeshAnim(INode* node, TimeValue t); + void CalcMeshAnimInfo(INode* node, TimeValue t); + void DumpPoly(PolyLine* line, Matrix3 tm); + void DumpMatrix3(Matrix3* m); + void DumpMaterial(Mtl* mtl, int matno, int subno , bool recurse = false); + void DumpJointParams(JointParams* joint); + void DumpUVGen(StdUVGen* uvGen); + void DumpCamPosSample(INode* node); + void DumpPosSample(INode* node); + void DumpPosQuatSample(INode* node); + void DumpRotSample(INode* node); + void DumpScaleSample(INode* node); + void DumpPoint3Keys(Control* cont); + void DumpFloatKeys(Control* cont); + void DumpPosKeys(Control* cont); + void DumpRotKeys(Control* cont); + void DumpScaleKeys(Control* cont); + void DumpTexture( Mtl* mtl, int matno, int subno ); + void ExportTreeNodes( INode * node ); + void ExportTreeAnim( INode* node ); + void ExportTreeKeyAnim( INode* node); + void ExportMotionAnim( INode* node); + + // Erm, dunno export + void ExportProp( INode* node ); + + // Misc methods + int getTextureId( Mtl *mtl ); + void SubAnims(Animatable *a); + bool IsBoned(INode *node); + bool WriteChunkHdr( char* ChunkID, long Size ); + void CalcChunkSizes( void ); + void GetNoFrames( void ); + int ValidateBoneKids( INode * nodeptr ); + Modifier * FindPhysiqueModifier (INode* node, ModContext**mc); + Modifier * FindPhysiqueModifier (INode* nodePtr); + bool FindFFDModifier (INode* node); + void FindEditableMesh(INode *node); + TCHAR * GetMapID(Class_ID cid, int subNo); + Point3 GetVertexNormal(Mesh* mesh, int faceNo, RVertex* rv); + BOOL CheckForAndExportFaceMap(Mtl* mtl, Mesh* mesh, int matno); + void make_face_uv(Face *f, Point3 *tv); + BOOL TMNegParity(Matrix3 &m); + TCHAR * FixupName(TCHAR* name); + BOOL CheckForAnimation(INode* node, BOOL& pos, BOOL& rot, BOOL& scale); + TriObject * GetTriObjectFromNode(INode *node, TimeValue t, int &deleteIt); + TriObject * GetTriObjectFromObject(Object *obj, TimeValue t, int &deleteIt); + BOOL IsKnownController(Control* cont); + Point3 GetAverageVertNormal(INode *node, Mesh* mesh, int VertNo); + void OrigProcess(INode *node); + TSTR FindTextureFName(TSTR MapName); + void ExportParticles(); + void ExportParticleNodes(INode *node); + // Interface to member variables + inline Interface * GetInterface() { return ip; } +// void SetExportAnimFlag(int State) {ExportAnimFlag=State;} +// int GetExportAnimFlag() {return(ExportAnimFlag);} +private: +// int ExportAnimFlag; + char drive[_MAX_DRIVE]; + char dir[_MAX_DIR]; + char fname[_MAX_FNAME]; + char ext[_MAX_EXT]; + char filename[_MAX_DRIVE+_MAX_DIR+_MAX_FNAME+_MAX_EXT]; + + int Export[MAX_NODES]; + + char SceneName[256]; + char MeshFname[256]; + + Interface * ip; + ModContext *PhyMc; + + FILE * tempStream; // Temp debug file + FILE * expStream; // EXPORT file + + int nbChunks; + long SizeTab[MAX_FILE_CHUNKS]; + + int nTotalNodeCount; + int nCurNode; + int nCurObj; + int nbFrames; + Uint32 totFrames; + int CurBone; + int NbEmmitters; + + float radius; + + int nbBones; + int nbWeights; + WEIGHTS * Weights; + + int FfdNum; + + int nbTexs; + char TexList[MAXCOUNT][64]; + + int nbMats; + MATLIST MatList[MAXCOUNT]; + + int nbMeshs; + MESH_SET MeshSet[MAXCOUNT]; + + + HWND MAX_hWnd; +// char BoxString[MAXCOUNT]; + MtlKeeper mtlList; + +// NODE_ID NodeList[MAXCOUNT]; +// int NodeCount; +}; + + +class AsciiExpClassDesc : public ClassDesc +{ +public: + int IsPublic() { return 1; } + void * Create(BOOL loading = FALSE) { return new AsciiExp; } + const TCHAR* ClassName() { return GetString(IDS_ASCIIEXP); } + SClass_ID SuperClassID() { return SCENE_EXPORT_CLASS_ID; } + Class_ID ClassID() { return KATEXP_CLASS_ID; } + const TCHAR* Category() { return GetString(IDS_CATEGORY); } +}; + +#endif // __ASCIIEXP__H + diff --git a/Utils/GinExp/asciiexp.ncb b/Utils/GinExp/asciiexp.ncb new file mode 100644 index 0000000000000000000000000000000000000000..5ce0a65d68f32bb4804ef47f38c8595976605e2c GIT binary patch literal 181248 zcmeEv2Y^)7)&F^K7Fc?*A$33m1Sx_btO#3SSGKvk6cvQo-C1^Ic4yt0rPx?ICiarQ z8j09jiX}#4&-ZC0F)Fb{W1?W18k1-=QLOxbzkBa{^XAR0h|xsz%?^)u&bjBFd+vR= zp4%!S4ehabtSJ#Jno=}zVz8|}wyHhU5^M}5LiM3|I5=(c)Tw=Y4LN*)h~$V^a=YAM ziEQmVG%y#W>|ZtovMKO&PJt29xpQadPDbG&8~;uHXlM2>n*!Ms$fiIx1+poSO@V9* zWK$rU0@)PEra(3YvMG>Ffouw7Qy`lH*%Zj8KsE)kDUeNpYzkykAe#c&6v(DPHU+XN zkWGQFRSJk~e{+M#PBs5OB0K;8wfcBjWwR-eO@V9*WK$rU0@)PEra(3YvMG>Ffouw7 zQy`lH*%Zj8KsE)kDUeNpYzkykAe#c&6v(DPHU+XNkWGPX3S?6tn*t^U4!n1&wep#z zvj$@4{~kEsV@hkNB^<}Ap?*pt)V?a5h_p7vG~N)LkDS5s5t?s=$dkHqSa4ZFT{A)4ZZ9-I_h5^0@2 zEm$0`?^vZjYga5a;b<5~@Mxwr;r4hW)|xDRpSgT3s&0?9eU4A6{WZ&AR1IVCaBwN-_XS=E`XIc2fcKtw?}Mj1o|$-Z@$e5d zkKB!?H=eig^ueqOLz~( z`!cxq$9r$!d*gjQ-b3)_-)`49+47&c60gXX|41tvw69tDABIBUoaeO@rSLYK>&1ex z*}N~n`;N;{pjQqFu&{OlKOLpM4R2NqEAig3pGfn-p@9SnS~c)yyz}v13iqjazkpI1 z>hcp}=p&`wwt_7;m!OhWB>VBKb3h1@3{I&A`8V7{VPi zG%yOqMA! zRSuGAGF=Xq8K~Aur9m1cEKRaXnx#crB_?gsE^$dnhpdscvQE~^@p6KkC@0CuvOzlK z8}dy#N6wY=ZfzH6UiBBT`Ao)??1&tp;d@s2$@WVih>wh0PHE>p7 zpeui0iK2|%19wZw8-}uWlEmR|i4FfFP}b{R{hfI1?)zTIWBi@>qrf*HOhp5oZ{k0} z^K(3o8_3zeYzlnkQovjOzb)VPmH*>EwfuL=Yh9M7uUtg43T0Cun*wPmpvu2$k1=TO zzEAfnF+$hvb3h)+c{67%M)n%-A#?0w?K52WUUEv#gE`M*gzmKObvbKu-gn*mq3?T# z>~Q@Llo{wR48#ar^Y1Ay$PjBD@c`TRy=5Ny-Thqt!SZh4u$;GC`9tLJz&`@Vy7Gs~ z-*Xmr8{^_5q@vp`-4?ibP`=x3VE6l7dHcy7_N(^wEVX`2wAg~DGY5uuV8Ym4o{gty}37G}K3t?GVi}BbM z2*>N@!2UOR9ol5bcHrH8d*yTmJ2HRUUH6N~cLKsBPIx7BUTyULJ>q>Yawp^Sd(g<^ zeez^B#zDj6Z?FWx$TTKT@)Z7HZ|0}`V4lpuXl#<)>f&?N2<-|NFT`kSf^-1a{);en z8z*PEc(K9@m3+!8L4PtTr$WzxO8$I|>Kf!(7hizU-74AS;-wh#HA=+wUxsmCt88%b z!!Zhs$zT^>D97Tr!}VV-5&VAQ;uRP_Hp!n{yb|T9S$^Q+RTyE0Wsxhd8Y9gXndah0 z$ZGtq1dj9>l!xAL34XV@c%970ZviZ6gCfk27=zo}?cT7w;S;#{p|Gs!{&P2ZN#2wd zD7?D-cE>pQ3h52Yn#Oy`L-Idz9THdLz2s)=ht_Q_e{Z?Se$c+fr!5 zRqO9oKi55o@k+V91@YJN#z>{T-2Rm-e}B1Cek9*<-N(t()*q~PH@xvE-A~FlT>Jp} zr93O|x&9}}kL7;p=DHsU!{$HbQCFYIGR>Z0_jmcH$n)}|oa^G#U;sVeu5-hmAuH@Q z`!HADA@VKj2J45e`z+ZcSIM6sKSzysX3H%6wt^qk1imNg6WBVgQ8>p#g|b8TvQ~p% zyBEO>X?5Way{$ zSt$G2W9>^^yj;fH6Ya?ePrFy3gwK{MUH?_GuQklN7xJ}xHEbb=$j=bI#*dIm_9Q## z#vg|;3fV7^mqf$hj=o@`2_G$&!_MZUIzV~_*Mbm2)TS1<|}Z!3baQMr*U}m;ol(q z9z}hizd!8($H~nuo`cc*b8?r9_kca?|H;0@Uy&fk`&T3XKkK>=kZl;5uXORfr2tkp zhCd!OrZL|S;^BJ{9>;Aq;0hn@B|~F6wF5p0&)Ik`#6Kn zP;@eF*(xjWMtS%B+4+iqx<=4SsA)R|qws>?{YPg=!M{!ge$M|kH%*#FC1yYr?SZ;F z8u2-$W%gzTRlAQ67!qeT5DV&#cmV<-w-W?9xPRAjwiF$LDcBc`9;w$v`hjR{t zUUf&_1-&@`G|=jUk=M!A<`bGeR+FEds7xey@pGR%HS6k4AAW!4%OCB0=)Sw3d;ZOx z+wR6XHdHTP@LzYx%a^V1{GH#i?A|dV6Hyq8P)g>>wOxiqEx(w*6PI5!roCYFbz|(C zb0^&L^dpayTRB@fbXR{m->(>73!Sb&{%WzO%OQ5BYRSQ{(Xf4%SLd$F|Aw6}zWC72 zZEwD_bLX3X{^!YWF8}z$=id9(KTrPKgWDch7(e;rojc##xeZ^TuUx0fV@X{_oBobk zA^xvA3!g=wFM+2}hTeP)z9SYCL9ee;J@Je2Cn&wXgh*#aW>erZrNBzGql5O+4HM`8 zmHl7Mp9kcDoY%DdU*SE_A8*k1e}(sw<8$uT_J4)tgXPV@Ol|*H_z)=y{8iik6+TSNPF@p3q)R=FUpon`)vq3?~fKa7Cqi7JjdaAV-^+(&lXv-pUAJ^ zb{5tLeF*rcc#i}BT)giA{Z%~0z^j1YfOmKBtjD_@_&Ug;JvgvR;Qc*ddAv@hh}hc4Yk z;C%vbYk!dk5Wk}_ZlHaWHci?wX}jb&l{U&-riy%whxX{((T}H{oVIb!=e+>Gv`e1> zy{0f;HuOgrw?jVd<+PF0E>2r`1oUQ1s6@bb1>g^`X!0<`*#`a#z#+VE#Pd!wWXwRn zDG%0|)v$dOh;Xiu^MjldJOJ*~@UdP#7W1^=S&B7~mm|D~(f_N>6?q!)MUb}v@uQ7= z9Aw^)XFGH~5pT{T{vPhX27C>855+L)FW}t-{8jMKj{bAde~I)u79HyMi!q*po{z$f zYp6nihlBr8xYxq{6Yvgi!S{lk4#@o`o&&%$8+~}L85;z-Cm?+~@f?A8ZH1g85&zkE zKEijo1^zd~Z6z|_^jXMTMIy@)j|*)q&W8H|f^_-)=GR{hp49K%Cr;ZrrRa{0>wkAl z!$%`_?D^r2Gv6Nm@Jolzxq0Wt%RhPLgJb@@vux)_+eh9%;^GB)V~0ua@#kH8T*J(~ zVL!V1{2MPAx$<}W6%Jqi*7mD6Ozt;xlf8YLwQ=s8(>6vQKK@}O9B|^S?c2_|bmM{d|L!Mx z!ygi9H~(0^wWz*w-(n5VxB9A{c|Bx}gh*ro{Ebror~YT>D_kku2@u*U7@l%G{}dBw zS^J+#iPZlxe#lf7Vp;oNdYbGerI^_}YyZofY9<>x_CFZY@{320-gtWcfKy6~O9x}Y zyKLNuLhbq6_S?5HPR-i?JV~O#F6@7!RC;CYe`)E}1tDwy^C!%|&;GY9@xZ-5_+;nK zPj+rw@zBl3WC^H#J>jw7ITirno_Kl$HQWJyj!n?}lC~|~0vx}Qhg&MwU_3?mWcVF} zu@B+dnB6%a{dvNBL;fYRH5>*H*U*tZ3G~tl8XgAzlMdGKJLorVL4Tb*QM<1iZxEh{ z*`wD?T+W7^Q$Zvh!8q%XFl*FZMg}!~1av#s=yodjZ;opE&msSSS`EJlI_ESg=RoLl zz0r9Ozy&5QbD-Pzjs7da&o!q^hoyHvtp>OBdlu-^O}O7exE03lO!&Rp=x`c%t}r^I zhbz~Z_`U{xIDVv@5eT=)=(Yv?j~bnS1|E)^$zy|FV!}EBVR6kX^=XDaj~hK#faeYq z7X7GkDc&O?|9+#-N#NmnHM;jly4`BzmxKOC6YgDrR|PfC@d#_A>ilqZS6}Ig@i6@^ z#W#A<$UG5rj{B%j=V2e%*W%4Je%bMVcKn|m|Nq|`|8qUPs+<0~RoU!VU)+XzgzIJr z_d~sNz-kQ#P>(#YpN4mU=jIU_z6>zeO_Tp1)L)@O4bMegL)(s~_mwA%-}k{kY`Ug@ zfO?ZQDDu1ox>=7ZL4?}_c0AHAVf|~ux)MCJeUZK&2sh1ZEB>i zd+Z5WXY`o|{+EmnA3)9m*qO-R8~n{i=AMY-lP0X|;8Jb$Zw8%nBb3>8*OSWbNx%K! zmutQW&j8Hz%Y=JEf7;&&{~2;tP6^)*_;*I1 zF@R4tdhP*znv8A{z&~1``HzNtt~saQnb60qr-ePOyKruU`Y*+|rtOwI{lK%?=yn+V z78rg0AN2W$33olhrHz;T1HexkBH_P*PJ1!oA%MBIn{W?=wZg>leaN9*m-K<~dx_D1 z5OhA#=)Vf^okoWl(1A8aEhnICw}gKJ`M)%ATnZlUvtV2{A}((jeQp6yuaVmCHGpY% zrr$dOA7kQmG4xq)bo(n{uE!?NKG2!=V!{^m9BSlXg}Xd$ben~6HyNH`;JLw+i=B|u z!}Paq20v|_l+)ent!%f{bMUBN%1=zZ^5K^@aPr&@{)>%1A3@HAppjpYpC=g|HiL)z zYDiy+yhnRH{RW`VYeu(20n=7b`f$XB_Hn}GqD_}@4&d+LO&BI_>1X5q@cNC;S-rcbI(D4|!?1(X#<` z?%$!FL8K?`>f|2|nd^<;2f?#fk)|I4`eR1_e?WikEg*j%g!>D;2_txU)#yJMI>5W4 z9{`whAmr%=IkhHUw}PS7=yMg|_l*u)A%Eap&EFR~f8dk__=LI>XHceS-;WBf{c*1L<1<=bHZOKH%XV0@8;fUYvs={A2KN-h}WT zkU7?bH4gj(XKQ*h=(P8fXENaT@FtAlThMt6jN+spjFjVC4&hwzpJdYHMuauo z^k1I>zu8lO`nR_aChi%wTagz_#JEX=?Acw`tI=$mqWp{BASf#RmQ7MxQ3=z_~8^ z#Wb9nGa)<;Sh>;R7{F6ayj}-?gVA#;^nAm}3_vF5c<8qq!g|KU>pRf1!03586FM(8dJckL&Znz%M*i$SLG$E7{wqf35Oi4Y=Qfju-4WI)CN7PPi_!Uu8vkF=ZCiHyk8knXIZuD3#{W~%R(_Sn|0|&n z_xrK@Zb4f8#N_9N$dlZcOZp3-f6wISG04x{ug83zi#&WnP{TWs=Q|B7D359n5O}(w z$1$^5^Atg*-9y8rD0gQV{zu`LbJGlK2f}*6=uiRvzat=qMYnSe|HI(toH6NB!Nc`@ zbl<$+Q)*tE^wFSmZkq5Uz<-9p% z+US+%Ox5t=;J>$4!$ZN(d0O%yN^)ON!+oIx*9VY368r^5&&80r+~{x-{FWF!dxO3b z-(Abek;jdk-u!kZ-1(3*%gEdgzXuzgUje+x$ghK(O-BEEz5d(WS#^5(~WMG;CbBW&>cEdoAfE; zcQN5kK)4+yte4@JXVg$1v|egW2jQXMH+$!h3v<-^6w>zu{c;o55eVxQqyI9%cNu-o zhRj!u4n3fQ*^`NU-rd=M2Z=dyris@>;NP!K>reprIg|IU1kCey7;X>zKXR0Ydm>G4 zH9FKF+;@!*D*$(x_?ALX?n$8Ez6jUs3q)Sck)Iho&xQOyny|hN*uq*6^4rjv=L8YP z^tNm^dOirA+`~0}CBia$1tC30&N1OGKxi)-`R9TE5u;}X__;=fGADwkWR!+`Augkg z%%j0`uLH^T1@Q#LJ>&0|d34gr0; z(We}Kxn_v^>;-*>8~LS_Z}P^k!O!(EFB24POtxEAln`ZOH$Y(cwIv2spR)gNFc{FPfaSmM{NF%PDQ(*M0}in#$@ziv1GI_q zyq81l$#PEMoWO~$`&67icwyiY7oR2<1uhB%7bySWpCQLvCsqIIIR#KlYGB-QO+R7BzmPyluU0y@`7?VR^}V$@*UxZ<1H6SFAH#e3fjowpsIBc`Y~}<}cQFUH4YmZf&<(T|6c) zTQ6JppdWIuFL)i}U(Ay^oV;t!FK+9ng5YJYzj$aqWl3<_2g&R|2tEi|9inF zGXlA&0B(A1raJ$3z@(W0Rty_~^ZefdC(Kmm|BioUraJ#u_MWNE|J?$2p8xySUU-9l z{GVr{M}j#h@C`mZNk|4tn` zGcX$N+xzMFy7H8o_C>#ODEj)tu_k1s1hH;< zG%VO-WdASh{NKGXD>YV~|I7N}TDchKm95BUekXpNTwq^dv(KpU>#=tJLi;WkzX9i& zU1~q-;x{V1cY*r;C|}>hnCx=<8rS{%a*2J39X-~`A9vtv))j$=UHndo2BLwRUHpf# zDsXJzd>8+btPC^-mH^lK+$~22jtuPY;`hjsz>+{e7yq#=4lEArK)+b?-z$p(ivowa z?)S;kz|uf;p`*_Ovc_IxSGxFv(qVVlxi0>YwA=0WtA{)M|06M+mvp9fM|}TR*4k_B zt6lsNS!b`aN4owWmADC67?k~#(Yl0QPSQYvp{8!{^{GM6q;J?SYy92Cg zqWAwCV*gS4Tm3B@d~Nt&m4FqnPKNDC`+q~O#P6Drga1|bw)VCby5av#hFU|dzqtT6V2DX*3ELW{Ez!R23iN=w|tr7e^2Wz zta$jd%fGkvUHPv3)J?B_tiND3f$QLO`V6)%$GPLvUH?O@H|0$k;eOANRv-LcTmqzeeiq8l|Rcm2)}#X^v|D{pVYI;P;Z7{|c>M_&wy} zMOIJzX4g3JDYnkVdFdluyu>;OC#WCg>bJn+dA1+8;gwl4@Eh*tmrCnFc~D+!a^zK6 zhvIkDkq&-@^&HN(8tJ;%STph4;Fix?>lc_6o^XuAUuS(HpGb%MJr`Ry%8l|<*Z&eL z55KEje5o}XzeC;l9cgvL?_M{*A7%YselJm1{xS~XttT6t@RnQS@H@=ar{3Cv^Ym7_ z_$upXm`R`E=BH-sRe4p)T>r;f`{6eLWJjf&fBSC$|#>c6r>t?D;?0 z^MBmT@Ff8MiSvIx%l@D2`9EEJ>@N2IWY7QUBE7mGXZQd36DGU=$DbmfAOGr~|1-ei z_^iV&w~ny5rbzeq*V{*0M_K(9E`txTkGB_Fi!IKt>hbak_PD^f0Oxi!exkykJ;?EY zvR#bdCKume7vVS8#XIeM{5SyAsTY70x+B9sYIpC-^;$`D-1&8|)A98?4_(#&5NE;Kwxr8vm}suSLPi?*GZ2{}IGQP% z|K0sR!CyZ0wch_zdgoIa_y27C@l#*;{-4Vrljj~;s5`%o`+shN{{Joef9^qCz7G3; zwm{F_+5htb_%rSQc@6Zh(*B-vju*6E+)VA7q$QA1<1_q|G^q7tTq2{-~Y1|$%cDabP(^rNwomvAor|Hmi|qvs(!OPus^ z2 z3G~xUeD{a^9~#|;LATe9{Cy#Rg9+D$gsV-w9)X^OoV?2Qbe+XIQ-;|2m`F ze$cJga817mJY3&Pp6`HXhw(cQ^esk*0np(!Bj*zE%rIfy51zY>%>5yg>u4$GyWr_I zSi}9n!~554&|gDeh-ZzH=Vj1e$k*_9p)R|egit!);t}bm#*e<{3EyK<7QMls_2X>s6ya@83Gy@azp9u6-uY zt)QP`WWEOa923^LpmYA0Je#2NnP5ztUmuKk{?vFjR zJS(5@|A1$O;qL>O=kt?39(tCUxa1-(Jd2+6m%zib>j{s5%xa_Cg@C!1oAg@%kH8{* z!Vg2wV~zYh;g@IAll~^)QAX#X&{^L@%)HoLxQ3iOH$o22@F&~@zw^zvI0SxqCOzpE z;u11CTm*ieJFoabUs#~w7ofxKhQ0-Un~Y2wGI?hK`R@XqYuO3k06E-uLO2STXWtXH zA#=S6>m1N8GV~ts%egi3ybbsyBflr)-)?k174$0&{~rN!9X$DOh8&*lPk0;X+}}dD z1$5rCK==osw-;#mdBmm8q#I7(QTMfxCkJ6&Vs!f^{PG@U(t9I4d9MKBAn3Ol9sUFt z-qk?*j{yJD=(!d!?_D6hKlr&nhHyJzuKg!`J>c^SH2f>*Q)uM>2{7+BCeOYI_kAP( zdyvWd1W11u@E=WBmjLD+38cROn0pflUk&-EnmA5F9J!~5^cd*8PlE8h;9qRQ{TSiC zZDe+W&U+BZ^H;#UI=l@X zcxMFZzXJU&6YgO6<$cwp4@9`!n?d+m$mhN%!mWULr#Ru+kk9>MgqMLn(dc=bhkV}uLHPTS!@X#PmxE`X3G3US z_t{g&eLVCZhj5t=vjFq%Z1V4gxP*)x-b2VcHc0;|;6ItL@(|XshUX*Dxet##bqMPo zqw~AaxyFQh26&#s!d~)lx8oS2Ln*@L-6W*{5InqJgYYQG;oathbD+<``Hqp<4fQbR+3EK{$mCrmga;w4w~TH!bmP4rOp~S1 z?Hm)wk0ED@(czbnzrgS`gXcsO$IHRP`$Q=7*MP4yx-9@d?@=ec7s7qb=(z)Q-bYXR zA&ASBCae$Pm-m%WhjozOZ?)#hVLQ@I!^o-X&KB})1kZ0wT)qXFymN&zheIasKq0(8 z!u^4f{~qLj%jo?70P~I$%DIejG5Ws&I}O+ zr$3%t{Lf!5`ilMWkrpl=!!ZRPcQiE1$+SBxO`Pfd=>OQ z!21B(2k(9G?u$2i!|;o?2ak>CK0Jd#9}K$lhh#;(B}kn68QK5A5_sS?b|2Ul&+yQA z4(@aRwmL&YAI#(66ZCrn58uw?;NJICBp>ld5f0%}=$HM=ra(3YGN(Xx{I6@_ z?D$`&Ls#SfdvF$7cKpxtn6u-5_QSH{e_dX( z{{Wn6{dFGym*SjVXZ*ho=eYkT#{boup8m>?|4VUp^{&SMv%vFZj{iGx-u$k{{}+Jg zix~fJdEw>2|LyTV$Nn6ffBED8yJVbo7FI=Z{9lIWE*WpNz^Y8Ue~#F}yW{|?S(?=u z;2NJ`9VJJpb#WS>Xq}83rOpUC{0CaaQmkxs+I^DMDV^$!evMDIqOg|J&aUw()^V^O z@XUXWPqo4lR(2;?LJO2h2=W)N%T{6wO6*C_^n^EJ_)%gmqxcI^9ybYeMsNH8+ zH(^$T^NkvxY2AXE2cC(q!#l)kkOpOk)#1&u?!-+IJg-vo&$jNs3?$FK)OenCJ9>XS zM^od6s`Es6R;R}2SU-Rzo_1O-|1hf%w=mJ3tMR#3krXL=w#MgK1yZ2wbsEpNj*(;3 zy(LyvX_vX8kztqWu?JAqlBIy2eYage275fyU=sC&@`_ z&W8KR?vhgLad}+LMQFUtdP1I1^LH9wXgw)Us(D$BSE%zYIDe(_YU}5)EOE|4>vM!U zZ-Vm&8n3ZR+eMGvVBr=3iu8CYPx@Q?$N|t(CG;*#kAc#A<~# zd;1V4zDup6JwGHRJ@ctZ)hpo3U>%cp4G~Q&ri}PoAKaIwltq*X14sEg;k66FK>>6#}196_( zUAP7ASC|c8`=IgF*7KM(pdDX_AGMal@_vF_K3l9yMn9GtD(8fpp0@oK1_k_ff03MV41O)-tP#2P!I;UpeU#M(V*B+?pBG=`guZ_PcW zC~sMHO?hq2vW`R~^U;w7t&x_6;q~#!4Q*}SfIfrQ!dPqgb9gHXwUkFzH77oY*X;S9 znLlFo{LioF`JbOsE*Dj|$J!=0W4hqK_xw*p?O*uo;{1=dkG>lBjehm+|IPnoDfFtX z`{q|{PUP8>4})HHN8Sa!cC@1%viit9I1P342~8i%Fnpg*G)xk___nsxQ255GV2 z<&Sngbl=_2J^$v;ZFl3s+TQ=dC(P`%JG$X3<T~m=w$NVVB!9QzC!~$5rLbI9;)PQ z22ME_;C;tsSeLYNh&unX6Zq*ErnKSBieV+*JpVI)xa1@try6)O-uZYhh5J;zdH!dR zZ{P#)#tRE<;UD3Z4g??gxAz&U&grq2J|44mhGt{gN} zo&R|OaCEt(6K|$}8{Sy@fcq`9ylTAnz>R>1eZl8{I;Vc_>pAyxELH@}mb{enKlefP z*@~IXHuMGK=#O{E8d)prWW5|OC&-EDk)MoSYo~kzXYHMX^X$%(^W_4$P%e^-)%l`_ zV)mNfXmb#Kvr!_8@w^E5UeNgT0R9L%Z(Jk9^+p?zb$^BlR`$Dj{~-Iz98>d-6|Q^X z-F@s6A37Iz6@iy`FR~9D#B}r*wf~=e&=a6zCm!C}!MmDxzuas*ysM0Bur|Yu_qa@- zrSGxgnf2$}IEx?d2OwTI{(kf8uLe)*_wEy??VM6{$Hw))JEq~I5j*z$aL1W%4}bWj zL+9MQbK~Woyz;>@f8JTP^P}w}?;mmTg1oWAr1$vqu05_{X5O$LU48D27mQr_yZs7> zFMn(M)f*=Fo4Lu}{*<+G?wr#$Mjt-?;VVx3LEX7MKF+yd-h~@B-Zyf}7rOT!G5E6n zkVw1vS5v5Mv|QDG-(n5V$C?V>{dpZ^WzYZk`kw!>UmSHM+5p}W@l~n=c$d-Vtp_go z{Pn=R4E4bIzItE{>Va*l9yt3;uLpu@Mb*OgG>cU2gsK{U`RX4&;XujB>6D%p2+ks% ze8L(2kVw1vm#H3D&==n+qLmql;JF{rSr0S_kY0Cd2e}jEC9KNj{fT=0PIp*qa62m6 z4UPAZhp<}sI^l)GQ%#jxlUf=`^!T6D4X*s8XpU5;E&Wk1C5Wz`RTv2cz2`54}kUTNp&xx#wTEX z*0X93L*o;1r{?|A&E=njwZH#V_sweeDKf>LW^>)Q#;3x1^P*ZGtMTbL@A@2@chqQn zrYy5tY~J0e@maVR`8tbx4KzL*>#43%cfiuVBj|_B!fz`ak==mLl?7O#rSChWdl9Tn zds*hbW#GlqDW^H}SitAYndps}yQYAb!g|)`+|>!Z4A#n!b8i>$!=+JHIqUs^FGPQ5 zjoMG5^(mL2y`RlJ9~!Ta{q6BK*OP0!5`CxH&V9x3UoHDu!z|vhrQMIf8pT6Y-$vs# z!t=k_Z_#+IOtdH3vk=*ZkhuuYF?gbQW}{%ejyKnhac$USqzPft?!`MFVbtJhz|)53 z3_K1mdFCPv@;Nm6e+=|D4d3gC6Xl-GwUM9`|0f<72hH(IdCo%`b*0X~K^UU|Z$PoW z9`8Hx(0w)J#PN`aVK6Sdlb84l(Blm}AL7Xr?-RgBJ)goucp3C_;#>uMBjoa7n6JR? zD$v-M9EUfbOn3i&18WF*_b-B1>n6ST{d&&<38Ak?o?F4ctGj=BAFOltFW29XF4cZ3 z(O+Ttp`5Pn{^gqBOn3j*gU-8u>6hy||Fl}e-n)Of-^P32ulF9{FXHZB-bd}+{mZ*m zKhNF2)Sv57cjxSnvk(^NIq3H(z`MHpmur;2jJtomclzun>Y)9(Id+_`>tchCOt-uLUh6Tj_)Z+-k^dmTobMpaHRi+2`tA=f7U4LQbncHaZIT2LZjZ^DehGN) zGhwkTErkt${QE)X8D{->=_#4d0NdV~`3x}b8(6?P8*-2T-s>Vw}Z~S2aE6#h;OETz`4+e{Q#vW z^x0KEfct@V)em5M>GT602fRD|fS-XsQ$L^)boK+NXFl}I)DL(VJYQx%fcx8=e!$g$ zKTkh^`d35$-RTFgujcdvo(8GnnU1Go=z zclrT0f&Z)A4|vAZb(^7Qf$5)}j{Trq)z9;I*#ocxNFbH+v^`?BaA)UQv zfUTdac_yQ7f2BslA=bA>Cij7L^w#t#kaPIK8h#Tze>D262amPCrt?-<-cQGRDa88U z$mx#yiuan4z5(?6P2JTK@DU)Aj$kBWbRL4R&NAUn1+2%!ygM6v@=RPh5f|qSFpe>D zjSlmm!wwUdqo60x>!$q60n^q__;28!Izhudz#lg8dJjCbhmfZ`ey5nYG%_wm=Z%1A zk0;MNkaLC6b29X#ZJ+e9;5ilmVNAtHkD!Lvfd5&e{{_(ht8fO`kB-y2@!sT9jn0UM zW0$}l@$NFx=syDbpKHG3sfgpvxJ!$1{5jH%_aG5|5%dR5njDBUx!$D79)JtXH=2Vq z=e>sHUkUylCQUXYO}N;Nbnkg!mm%B=<98AzTw}iDYtZLD6YdCv z`>4_LXQ1;wEAnrF-}6o0umP6@HT^_{H4t+#te0+sK40c}U?(7~k;OXPLg>(Qo`#pA zJo4T^>UkpQoCl&l4?`d4Jg}{Re`54s2%dRH=k1{H>O3&+x$iXb<(~L`VN+yS-C(Ji zY4jbhrq9ON^eqL0@UUQ4f5h$Bmr6 zkTc83;Td3?jBfSd-`D6fAMzI&`E{Us&k5^0QHQ%f%2bC5_X5aSVZ!YN{t6?18|Zf! z`3<073_BF#H3s#=DkHxZ^xN@$N$1X8&Mgw20v*ma^3R7H+Mr4A2EUIOnIZVCH(|X5 zo_B+qX8_{3(#SayJS&l&xs)IM&z%SMiqUx)^ts%`{RqH!nK+&e{ke~paqNL~Fg99D zEOeK<%y-PecjQ_B&rq@ZDcDX>uOo@`%y70%`TWiSI=4l$dz+LA*vCuH$$#cz9O=)8qm8 z9f`Fkgafo{mT35J@H}twYInf*n6&EZY%m*o9%IsW2xM+I`jo@(Ek@_PpwDn4zm)P# zp8PfVXB!=QLWjeQo=1V_853>*8gjic^?V-nUf+BTp9Q}=jGn_F{}B_`4WLgLqs_3*nQU&G&qOx_(z`kjCeH2QZ3g?E0C z{$ud-z7WDc0?f7SgttN_?@1wi6X=VK&W`~eWx~1!Fz0~D6TmN)ui+~Z)(7EPFz=aR&N*S{!>{+8FzSI!B$ z7;<)XPS_jZ`J&GW`!i%_IvealNzVVXZoC5pZNL}_viGzKGyH!i9w**I?zQ%{_aH9B zdrF1%LyP@kjl-UAFS6f*JdO9oX(11*F`vf!V|9RSvA?SEfwC0$S95%&@jaxQHQwU5 zR^xlgMfMi^7p}a0WTG`6H)J{P`{F#88ti~^@S*Z6`z_ndkwCu@@(cTE2ZzW}kUPMg zM|~1V+#58}!6e#6k6x5L*JPWrt@9_M5a!?OU77v9ylpEmW~8TTjnZTHf3 zB+sV#DvbO8lVg1(H(F{mZl)jY9qV6;wEB0A^*Lw4u|DSf6&X-5KlWf45VO z^$W^_wRMY%%c_nE^74v1 zTH4@L9gEk7TH2yvNjkwj)}DZ4xIL6cR$GO13Vdp?&eovOEunRjn@3Awbu5Z$rlx2t zl#obkA{fzdutPgd)31Z2DAv-}kq8$S#?}d*=~5SI2`>&sJHo+)%nvn$D?)9~J1=jI z6fdc&DGu&`^z`LY(illpgyYS{p+rc^V-2AwHsUD6>ynq(6b-G4YmX^@Ag(bSh2O>D z_C$DHv4T^*VoMhp>SArNOwMcK;iwcw5(Vw;q4hr5O>|3@7EUKoLTxx9l}P@^S_PL! z;t8Ke$|6^~I8vEPb)kB}Gl%e8ncvh{9!ex44dL3z@nNZs;R{Sx$s4RkI&0kVqOrN& zRLILy@s6j`nj?+jR0={@0q_II8B$?YRe7)>8VVdGrz=;f&n zjE2@DIJPa-sUo%}EDbSiuM2j;QP9>FT`$ZD!LiuC*PQCV7WpDVNDgYDhwPd2PfiLW%asy6I3H<-|oI#9U8# zc}O0(i7U{2A1}e=R1b@m@92<8h|BDxeWR!R&yTb=qEw?uMVcbvcF}NN9t&(;g`=yK zRmK{_!3mL8yr55GQLGhZDG{8|(%=!M>llbIVuBNpMZvAV+6490+(MR@*RTd9(iB;h zO`Y3#maDoAlIgO{9 zSR#}#`OgFA<+aBWl0rzNQB>AR;W0Tdh2}xzh>lPqc$`}Wq0*jfG;*pLt=5FOq6(xg z7fCQLFRsd|my$>)DaE`zMz0hS1!?)F!iayC1}qW{m&Mek*wM(X^NCimC7WMXwW1~* zYAi?;G&Z)Iny8@}HQWSN+S1V)kF079HwHDWIE>nx^Nl67c9;yTO_{P=WK z5!v{>z+NW>XKB<7NK}n29Vy($E#K&bWK&gqw%UUDgt&epi(03`0D$S&H zNo8@cc}Z)dEGnz43%0~p$&%^@OKB%VD;Y)^8 z+l*eI;z-g8Iuf9&Y~I!$Uel&?w)!?nCTOjKYnAdyy^~st%Bw0Dc;W#KD`WE!dDL}) z9p5UvqG%`{Cj`A62POu)rEFcK38JePSCth9u_@ofQWR@jkL=LQUSK@5%G;@}tEddG zLi$D$=oz?etm`)=o^JTAGaAe0*jn{nJv1~~PR?WUj%AbQ|6Zh(w07VdN9q{}3VedO zCmAmX@{p<$&Ea+vhl9GHPZOOWP9}52pdAQB8&sBHR)|Y0st$ap;%JnGQcBAzYwHRs zi%NpcJffhuyj;|`2{tvZMYR@(ZYFJ`ZEI@KLv3FZ_U0RZDN-LK8F+C_tCgCqR7D1d zGV}%7S?q%o!V&Rc5#3XaceEj2#p6iUmNuus55`oI#hHYa9WC|YcJ+Y}wPdh7`@s1Y z&1HyJsI?)Sj4Qon`l|CI>pI$0b~gE6Df(HeH^d_nis~vZu8LY}1;_DK9a6rd9lfAL z6y2XhRFw+!!T=+A*BwwO&&&* z)Lo?xz59}mnT%sn%JexQOR^6eURQ|$P)L^r)&!L?snHI>3GHhSxCoSyh)JD22k~kFDz8d6UigE(}sNG+kv=3{RWD;%zdL(O0?59~R?B#hDChAnxf% zWDvs~$o@rY5+hX#)?f_0DojzHq|xYS=xNophxMs15QToJu1Hp(d|()`*#xXe;X~HLVRM47C55~|iuRx8k0$DEYyD?Oij{ZcZ!>$qm z4Mwb^y&=5oj%kSIuQ2q)TG~qEiKVVy&}~| z+LThGWfA%j2hB`&E#vHiDeClT$x1N^<>k3`uBSF`NkpAN7N{6uRTL~;QCU@7QfnF! z1_Oarl_e{ROBU3Wlo-0U&Q%oDm6z4lXqH3u2ht-B+ zNiM5gyuu?7z0S(YlJXUGM^=}t;4nXhP{$xEN@`2#6Ty|#logaqQB`@>qMG88`BGJQ zxFgr`S5`T{YDINbZCPDeRi#u{p(9<7u#X6(YAOoK>73%dwy2=I zM5=1a7APmDm+9t4w}+6IR~E1CsESQF|EelF9CLUkeq4Y4lN{ zqQ_5rFDu+2dJ0ZDIK6+?ibWV^Iaa1th;BpIC&|ZllBQ;i8>8#XBduuKqWItK^C!Hm zx#@Gz;N~(+@G~4vW2qXM#l-^$Cmc%$U(kydFTgMy6@>zV6FSxqH#RY9Tb-gU>TI-V z_zcch4K`qq2C+eO4$(3y^Xb?&^aM22j-G{v;>tRbj5j2eIh|{7$J(QxHW!$ZyMlE> zaZTCclI30&$F{Kl(VC&G#_Q}cNEn9sm8c-REa)BTUaVn22J|}9*rEE^X=+S#0@Q#K ztU;d$Q^uJ{`ycJrY~DarRiH|o31!-hnO=9c-1C(d$@Gh~d6EHjcLhwb9^<9QF%fQQ z!{9oMaXX7y$`}>ZxN%BKZmm~?F>iy)ff;LPHDK3~P$jmgbW!EPTCb|Db+Fn+S%Uoi z;MSs!cG5kXk%S(Gmr`$r*hzH~F&ID8uscbEKYy96FKKCsBv3NFdbgvp>+lj<>*+6* zvLwtAxR>TomXwq&D6OkaNimF}F{VpV86ym@Z=kCGWxnOigO?xe`<|`!s6)J7fGRJh zB||<*_klc+;srQthq_fldy?*lSxp&nXwLB{3_}mTIc+V;1Vsz%>of9GDUxZ}D1Yiy z@25_FWHhu(+1ef{WyLFM%Z@4$w9?aR3X09FMdKRupG{&qZ`iL9LW8mv87E9LRF`$F`HHGbU<#UtJ1LWDV=$O3jv2YM(KDX3=J`TrFMyp6 zL<2e|G4!`gJD|RF+0mZ|#dMsUkLGkKFgp-wYUE(P{Wt`}URffA7`zL6Xlc@%EQkJDMOkG*T~!TP*qNM_?6R_$ zA|aQqP?cG-T0>8>8lA`@v==o6^yjqmE|8a}rg;^vCw%um`k<*SBn~T6o956Y?m!+A zj0_&Eh;xr#+$R9-rgGImGZ(kHX`<rmC($^*IYF%POe%{DPvA^bV#=tvt)&n;>Ig5H)p%JI$bxzl`!mLhHAMpjq0ZdBvW6kuC@GzJqxnd&7K z681Q9j@x03a<H zM*+;h)1mrLifs`_CRel;{{J4%#yjjVsb-)b4A}wG#(rgZxfla57n@}hF z@M1M7QP&*7jE{o`c9BAJbWS1o(+8hvQPcSV%6D$|V-;ANu) z*=0k4(F3507%EoSpQ42{FJ+*4K1o5Gb7CgrL@{~n6^{(gxf7?`tLy+yyimQPdr%jv z2{*Bu!1BqSv91k~j-172l7cVAUvVVf7R3^b#gXt@bQpsZoVf~JsXO8knI*CIC~`1VQ@*rw0VFxT%9OCA zH-}!(0E06cS0o&46L41oqe_~ZD`h5)V;KTpt~3@pcce774Hlu6B${X`z%--N#E)es zMMVttK`6>;I@mg$;C0#5v&4$iaZrWPWyAM@g2>DG#6@VK*&Wpc2F1)*kIWaGfJIr3 z0I!$2ilpkWg0{wKn5xA|&dKcTl3}`xzzpn&ap^^zNl2!rsh#7C;`nX`sR+eayB-|| zzE^?A5m?5#eOBr&MXgXmyAwgNU9ZP%Of$y=K0u^ty^gzgEHV}gDKr%^Nuc7x=+N(*4C*=cC#m-xtU1sB0 z$qtxd?#j40NM(2C-+)P|79%J@HA;zS(Jm9Yd;1iCtD3(%3oyn)mU zHmGDTLnpx(-C(nhyr6QF)A**Q!Pl*ZIRtTaDEO3yVnm>5xQ>e5EuN`lnkCHQh-9!94P zr-OuRIqt@DX8C3nK%7Jj-|Y+V~m`+z~W0VjV82qV7)RR z)PSk}oEWLHZV=qYzy)@#jZUq=?r3Ij-ZVpV8qJt^C2V0br7C8!b&np+NHZ6;Ip5WX z=j9>M(r6vD(Zti~Sap$+-eHz9rU`2A%Atd^teHaSg5Hr>qiquDL3HV&VgVC~TJL9! z^s#nKJf$WH)*3QeWK47ESkur|uwA;SVA*#~m&V9tb_qAG>cWM(m2ypUnmDC)MsaF2 zOJ>K1+=R7fWZ*#oBZ4k%9Z>SOBz$gtALM|m&ZiP#EtCbsF&u)#4?Tk znFIY)jof5OlkZJ|GW3WsAzJ`D9J?@{wnnvBSoDkU>+BxD2To&R1E#sSZYLAR$v!{n zGTb%pup~KQVTHTTAD4C0(w0Vcn=y~)RyMb4CDB-FnC?j%EYNtTJE;LdS}1A^kWMwb z3(_3iI34B>AU$Tmv1Qt)Gm%l^Gm+P@4swd7M~Ye*t4sHEObB4(TP7|n`OQR*wqzuyvu0$YMT9Am z(J`g=Nf+pBa55R(o5;+N2KN;lm+F4<6kbn(aXFpB(q#aj;$XgPN*LbGO^So}Yu9zO z!~DKVTN`MPAmV;AWuZu+sJxKImzRgEkb#;;%EV-npTWdwCvhy!nRr#*ohB6>IYt56 zn_ZP*pyG{MUY-~8Ik}#Vtw6UXUc_xnQi(0@urHM!R%-fau)b*Pg&B8xF`tueN4yNG z6-gx|KT;~)X(Xy?R>|z-BPUyrRCc%RDC$V4G@?Wgtnxw%j$Dv}hY#}O?2)9fAgS7X zTrEBvJ@l4oslGfSQXNthQ9m)FCJOx2 zxUshS>Bzr+QYxP2+?9r^xTgBWc?X`oSElyT#n6kmg)o(<(lM2ktbR@L^Ex|KspG_j z#y%PbGhQEF(`PE(DMZZwJ}-`wkDM%Bsgm5XRgg%88k*V6D|03Rwf4-+rb?VG#@6jrqNYzBh@eMNsg^G&BZac`W>*Rhz*dRT*I}9MOes<4KThqV7AJ5 z@}~eT|B4w(>)9qh?nxAv2iviC<*5-{#$v1LA_<uC zohL+?%u9S^mdJ%v|&U%jBiN6$d?n-g2Gi9Z9I={S_E z3AZLNcb=@qbt!aeVpC+yOofly;`S4L4!Wc0CngOtMrsO!Tf?W6(i9fe82Tv51ob&M zrBM~sF5LWKCiA(^gIiXMBWqv{Wv@}ElMi<*tTejH8A+6#vTGRe|BxrxSgP?$_M^IJ(Mg`St!td}|+l$wKGIyTsP<$zw$ zz?C4S1w}IdQevhf zCEuam^GRv&R1V-ymTQ%P()sI?<_fU8Hh?a;u{l-^nw6H$z_JV8Mz zP`8oOHL8QAv!mA4D+Zvpcl4Id8A^7v!0UFqx=_0bGmc8(l&QybsSSE)s93$tb~-0^ zDMn*WP5v*{7HLIq()ao5JL<6!(@%}-#if37E7wLfU@4^E0sMvQ*%0_0BCYNh_B%D9 z*SV-nEpGBVMOyVXN41vBr!WRcn78GELJEkdd3Bv>ndrh9J6>5|`u(<{4*)y7Iv&9n zDoaEcPC6T5?Y3VWrrH|AnE1%x6i0U>T-z4KNf5q(@Re~cN@H!PDNQ{%VI>lGD*5~< zX;?0&*2-mc)C=P>I%SHj5~wnoFQkTO2i7@qNKiy5+|E@I=@}6F?BM3C^JBjwt=eE# z;(l9S0BdvzYPg@#Un2r!{{_z+weetXy6mDD)=@fZ0o4@8bjlj*4@~jtoR+2tGqZ_f z@=&345zIzdNUBs)i!>1L5C@}a0_S6!s>A7vG@Z20=ya#&*$Qm`_awJJ7FfQC3a1h4 z{Jb*MvPu+7-H5vDt9&6lM~V5!-m}Bpwh>)H$itw?uYj6T_DM)V{T@7vlT-M3J`u)2 z82I?eTs1})4m!;&Vp{t8mpA)Fzz9Q%S-R|Vbk;2T$j;Km%w(mE&nayYv(Ft85th4F z+3%XNPTA)Oe<=NF(MHmMY?g2Oyx?1P>89Iht`tZfMCbX`wd>JVbm^);pekA9NmI(V zK(Q8GBA;p=4!;w$R+81KQC)h2n5pmyP$yZbLdsPSEP86rmO#R{`VKp0{HsEJ!b-TP zR`&(fg4yJDk6;2@P5oiJOWE9o&1PLQYI=1t+qxzk;HFn>V|id|7g4oo{zcDSl~1z< z+9x7o6YMIb7ccv~xUOamb63u0DYMVnSt{=%Bk!dyjrT02_eUGNYB96BjNT{98T|Xm z>N6w%py0S`<6M*&r~s$#hHSvL#vuRJBc$_x7cycJ@Tr$ZG=BWIxRF$gqY@N z74+(kxHb*?m8%S|1rLwZ!bVbf@VnQ9ITq4kQJ61oAn8MRxFDt&^BBdkwXJ?8)R2t2 zSu>NSF;dv$oGFnIil4(QSqZPr#IGY&xW399QK>p5sDb9HAvRjjq3sRiAdF zaIJBux=ZXArvsyoh-Pv%Q9Qi_XioOLQ#i5xAx7_h!CZTSPpaaOF$os6HiHEfSw>f{ zcZQi(m|X7}U1>5%4PEaMPBgb;YTvIb)_P*?e*Dw*^)O`&(&NrPd78qG6@#f~zRd&< z>J-e<$4wZ%kBx1jDB?^Y>}K$}sbMW#sf;S<6V~`y*l(|nhFY|yxnnT4RG&4jAj>X0>4hH<}h;=6R`lL@i)#TGtf5XD{I-CVO zj8u1PaK=x1tZ?#QOmq5!M|@$>ZS<(?3rJT$-2bd5gnX`Ad)HNsvoiBB!xyg4FyKxO zl4?8DdfY{2ekEysL4>;)57M(Cbpub+jX=} zOm{2%{wuIOEAnX!*b^D&VN+opVN(UbB4VPK!2*MQm{IIX@T3qb{V&aaZ{ z`Wm-P<_k4O)XA2~w1hFD$5Hp+RoBuVj)%)69_Ae|4Do#=r$?Hxigg@yTzx(ZVN_k6 z(VK2=%XF=umNLrLsQlIG0|YY`Yu6%)3dStsNy{DVALER5#adTknz^yZvl;V86B*9r zHgz7G9zppcR9VY|H~l^+`H3t`nO0*_yWF$#2G z_W`@=s&-I4;8q%5SnoE_O>I$CYG`9DdJ8agk~=L!>iDCBoY}cJrYjKC*j4HT&tf(G z>DQ|`27|ZLY>!=o4cq*<&I+`HOzy;d7S=cE^QXeoJjAqVnp&D){eh@yBju8*>fp># zmyXCQWwdxUu`{4|(xh@BfkUc9=$Ke?_gr)Cx6E9 zK1~-lPJY?mb$Na_?^+-|^g`)#Lh%#yX3})3sx{NedeKul9mQ8|GEH|u;-nM(lng9g zE36X5FCqO?yX~7JiQmId^nC7gC-;M=JDE?O?vN=Em4(w;KV2{P)2Dm%e*AP-@8_pB zs?rTH1Mt36T!$FM6-6=-Ki&g4U=nd#ekr}I4aAjuj`Xp9Zt*V40T$i6%M;ci_T|KF z;<+;0zR~7=kea`j+-wgE97z9)zmMeOLMGlfs@?m^8SCI3@6Hmw%Al z9r(4zRrt6MEO1w#h5ombe;+w5ura`UAho=GB`WRuj!qTcFzJw!%pHC<_(w>s)T=wb zoV&X|&--44r|$SN??>_80G*tBzxXEp6Ffi1Hh|kpx0w5A_XPbveZOxm(!{ynSC#*LS)O$H ze=f%(>AlDI9g|l-%iXj6LtvJ6LMOMC+GkQ^tM8vaSZi=bV0bMZaoA>6XP82S&i^5K6^IT^p}i3{<){Bm7K1`^fVyegI;;w_FDt-+oMe{sW~ ziZ9en#v*@c{%N?cezXj7@q^I_jbbBe+-IT3Hd04J{Bt4=tg$1PBla2_zsQRRsY> z6vP4o0-_>zt|BTbDA;=!3t|Di=(Qt?$ou`yoHKsJpx68U@2$7iyYsF2X7|@q zGiC2R(g0!IPyeesQ)xf!-9%C%TFv-nQ>lX@y?} z2WLoY{EoTw)1?!BBOE+K+TmBj(aVx9_;LKO{W%Lgy6#zUWsFeK@ue`*thMP;|Cv%8 zzbO_*d+}CX%;-8suo5aKTKIgp6=5sEmWM41TN$}c zf~_g$`T?S)8;TZbB%0Ykv|)YNSTQCE_{|XB9JVQJ6W})%E!Gm@tpRrgoescl1sjLB z_OR_>JBu!FA=icTF31<{StK5L^@hI>Y){yJz)yrtfb9<31GcZ|ivFS{ zdx@SJjJyW9dE8X=uSD3nu;JQe7{-_}uoGa<sjp#$$f8 zr1|E*fwvC0(Ux_g<@j10mg5@BN|$&HXWsi!wmtAOpRqwctS7@6Pe0poa{wRC6V5Ud zuQS}9u%yF0*luS4mk8=i8jK@u*f!DiMR5KGgFnrZn5DIJNxVtLJUx2-Ukq!O5H1Vf zLA!>Da6f?G?_f;>I7Xh5H{}7wAHmR#QDeSbFO?jAaoH++!#O_UOUq&O_*55PLAuFs zzmL^N81Kxc`xq?Up|I?K^ml+w$`kfM5XSoFz`qc7CG0h@n_xo|2|fQO-1wy{wyh|f zwzP2J6@_a>@I>x~ zPS*Y(GH)#EjB=%nd;sM-nIm$ZDK2~DQnO|LvmhykbEEv1C zPENO4+w17x_z^DL=&{v`1;8x(+2df8QMzP=@RTj`DR7n{yWZ+ zUM29pXV{L(`CQ)jzhght z&)jeBb@;_(jXtV79e!~+D9=a}hhIiw%__6p#h)jqOj&L2;PSFV?v;yB9!tN1Y{PuM zlA~Wq9yG6*_Z+<{*nc~uLtXl+a?E^dO1k(6+N_^GgZf)~btFm0=v$6{UCgg{%VrlJ zE3cVPO^(a2zC154$>k1ifc?@pFW+0kn!*O^<*{Vu*eRx>xrQP7X^pH~Ne{`Cs*G34XQk z?-c3b==H^F;sRNO^4aqAlUiC=*EzVq#A-uriu`PRl0To^=+Y0AN~Wseo|}yy zMgA5ZERD6XMqGV|NE2JKWw7_QHt-(+&iDYoIQ)FBq*EVzrG3t` z{=%3etPk)H1E)Yc2AANCHX1@y}y?Y4l&;^?Fyvjc+0jbhW2dP()uF^zc(nu=ZHBL(BA|9t3f$0gg+}NLk{pC37%;w_=g8& zz6Ji(L3!RndAbMmkHXJ!koi_ZzCQ=_?*Yx<0{=<)KMV4@4*nN{@*ILcGoZf}^tpZ~ zotCKMWkGqe5niyi4c`m=VL`s9k?(f_&5IHKNkH>!_^Sl?C*bFrpEO&8W>%2)Tco`z z$ag#F9}3zq2L89vwhH{_vtJI!SF8#PN~!9W&MlzXAfR6WedErcoU@Sj7nFf%pi>n1&4RK$13&joh!cmr zrUf{^A$(6z&J!qSB*-@f`3?#4T@DIHkK=oYz2yb0Ff! zpgbQ2b*~AU+}mMzdz4|(FzfFP{8xf99EX2kK=WbH{60uq0An)0YfRf1H1`H=SQa#S z9>Z|OuWL}Yt5LR9K^@P4PP3qFuY!KB0A~wu76f&?5jbf4@=$D*sabi$@o)ZxtJymi7oPEG~FR06Ppvg5e zajF3;m^Wa@rl8yt(El0quM5h&9{veIU0z{bg7SZba7=%FI$we&_rFN9GiVNiiyeK~ z!hlM_76$cV@;t2!p}$enZt|E96I#)nZsXyf9A~B|NL>$*JDqgdh@5Z zeq8jg#|}T*FL%-DGiQD}a~P{I?5F$dlEjp$DDtP?3pxTN`~mJeD0WN?T)^ev!jU>~ ztD;J+;f?@)54bxJlLGfs#7}}tyxDM{!LwcpcMs^?0Qb($B0Jzl5dT8pz7Llz^d;O% z*hifvmxJ4&uk^zyMiTbL2ckC&mLb>$O~Fp*Fzh^xz>el987*UEEM~IfB^9&T337$3 z!aQiTTqRfI>|rfV1lGy5{!TP^rz>MeueQ{|xy3}xc5y2^X1rz454ba45$zR^J&h_@ zBeugk-vMJ!N9SJ%Ji}>d_wNq@E`|o3YBhdad4-Bv_|EG5lxeR%%MA(#WHhfnNk^2UVJc4(4 zU2Boa?M2=O{a2GkMx|SvRpXGZm-UZ`SeJ8%yk>m74dM75npm{WZ^$>VxoeEbcW9H) zeAz-|=jaeR+ns|t)~PEpwXDeL=6Ji1ZU@SLW<2K1wJh#SC_l|RRk2piw0_P9O5hn4 zz&AEO!?CS2p5=ISO73NbhWn%mcy^qJO+`EY5VTbo{|3s+J^feFCR~%$s44y#2s)26 zL|f62pMmR}=Izi|;>F*STZ(7Hv-SK;Gtl4}FwI41n_tlO-V}W2>)E|2&Y6dF7r7F) z4BGUnW?0`;wrdTpCx1g(IG^EKj_bEH;BZdHvo)Tl@eJ)ao^3rm3x3x*-&|e^YZLgN zg3IT`Zzt!-oGbPK%NKF`6pR`Jij>WVyy`f*Lb{Y~y^avzg> zm*h#{9_3E-1@;H7NAJTv9@oiS8^2K={QzmXE?tIlan7+vQGVpXwE@@5TqAQ`%x_u- z!r8a@o!kIF=X0E+asI|RE5F0H!SeZ#cjROo)_(Yoa&5%#JHL6~!jj*I^KZ^CDguY^ z;Cke@FAHr{Sp2y^*Ad)z=Ng&wrM<8pp{)E?4n-X}pM4j8uAMq|vHQxK03QQR1ZCyg zgXg(C!~MAj)>A0wLB!>HC@1{f!{_-e&xkk=uQmhE3v{@K;@XCD*bdmk;~a55+GYvt zVASaq&=`rjcYx&?0M}Mq5SNNR+p0bKRRZ?PQHQG(XUK@hYbCdRvgM0ok%c8sym;xE zW(m7i%=>uU#9ynQEO+YUW#3iX_d&-_x1U+D{?}x@Pf;M`|=1{7Zuc?LRua>e>~J_dfaM)1NeIvFgCA>WNqMs@$|q0nYrp)rCmYZ6SkC|Z295ML4c~3NA~0Bo1#`Wgm@7>~zv7+)&oyqs^FM$- z8w-~)GKgbPY z3;xxk|4CAaIc5F_>3G}y-%Xa|uB-n$&61WoF(U)_p5m^hztKPK)scTA0Yq;p+{-JS z{8aWOY8K`mX%fLrTCG|}dZbU7Gv&`whCpI24L7;^bS{7b8VFJR{<_(>@!xpAhC&nC z%yYGID&GIHSrXys!2SP*_*Rw8wJ^{BxW`qxG~oi0Vg6yrS7!Nni(vn_mEpb-j^Rs?z&|S3 zdyNVsd<5`O*oT8Sj*C2d^!HYSaL)C(pBmzXE@{pI&H~squ(!Y((AkM~R8|e^-wglH z_^vbj6^y-Q@ZF@p1lEGT;2TK)6pYVDT3P=o;Pk{?gW+6fl23;I`r=7Td@t#53w(Z0 z>E{Y}agZ1HFgVv>_{;E<=ZOA_z~T7;{Z+8`Bce# z_2u4PqoACZ0f*;D#5oUT_$eq`OO!1kD1UjRJsdn2Mfk*^49!sn&U2X-Q*eLIM1M;_ z&4appf$}^Tlx;Hn{e!ZN0}j`b#P>jx^E>(r0P^2|kqAKl{ojZ=fqkDmIS})84BOJe zg`|^C(`7Ecs9YTLSPb_zZG3U8-RH*q?BYveHM>hry7Xo6{Ya`KK;HeDTD z0prIwDe3UfmlIy+817+O{L0eT`@!R!#lltP+n9a@xIbs%>XKAoX94baSvVqh7ARA2 zucKF6cIh#_#liJtre34mW3>1UD$TuI3eVE0d0-;jg#u8mEoK|#v|NOdU`!Q&hKOV_)e1OC3>9eTNvkc zUN4XHb_;ir-d=C-D4x@CSe`G1V-tCGHXxtqxX_>1C2knj6tqMC-*uzuoXa25B+m}Z zOuYHXlQ8|MDD(87+@GM{;duH9!a`o3&~1#qO5RJfCEguRy4AJ*PUz2f2mL%1{ha-c zINWo?29WPxj__6y>%SK7V^WZ|Ez)ic-pxyavjGX27Aa+0YwQ0U?-AFI46lxS4+i7I zvj|T}w&BkpE!WJ%QTP)h)_)Q5Vt;2i_x_#_$~hi5yAa55+JGk$+>V~VAH%SPz~Nan z{oFykBcR_O;hzO%cmVz>5sNbqdDZA){oN7HeGH}@fpOzdP{#!bCod5`Z8G!bCH`4| z^!txspI^oHXO+(+#u%*UEF6RL+>KJh!3DrwI7=>Ya6#;>W%#_~79V?`rjwy8g@ubs z7gIrxIk-6ZaX<0b)+lF$wXA?0I<6lrTn6PVn{WR-igMDjPvv#Vm(JNicNfaV(brv&-NgMO7Z7UyGxbI*o!(vkMR@;}&DKs#`+g>AweckUg~ z-wf@xGHB1M&~D^0VR!+&7YBkiOheiUQ*HPMz&R1I{&UIyG{^J(ul$eL?|*+mYuWEV z`LDwFKMnloLjlL!sEGG`{@U<;JHHb4-q6mU1%3NQ_&HYaxhS69f8~F8Qd2}a_qA;@ z3jLF^lWZRk|JgOI|G)A-wjKDz=6);Nrvl#h8X4CAAo>;06&PLuKYxAa`wK~2M;m?+ zILYzWkJDu7iak-{-+^%QPtji)^m7xfKM~JjZ%ym}|6Trv`~2AMi>yK$iP>eyUqtvC zfDf3*4EN;;UkiAvY?mVzE=?GRerLF^&G-#~Zkq+w5)@%I9JLr(kOXTrMyKP3Boo};n>{DgVJ zR0I8JxMd!=Kpwbj9{422u;}#l^Te0U1Cu8y8lOB<(Qw~9@HctrAI<}Rkq72^4Ef!5 zqcJFNV)-xQOYg=g>ltt@p)pEBK4UjV70gGB<-a6c5d3qMOpOS~b*Zx(Gtj%tT>(EP z;|t3q?8n&k9^qor)ATfUy+^o|%rG<1BSQM+WR{s_EdM6s%R?T#xcRtxNWX$yZ>~3% zpOf)bq^v1xtUM{!4A%q}KTZakfo7V6JIPQp)V$#6b&)}4kli2i%impwnPH|r&J1n(9@5YB zGvqz9&m&Qmnx!al7~e}8nMTI0r&+%~Qp?mbzC6B9ub;%5_&^??@k!FmG_&&fe*7Tp z#(rH-YBfk+eE=*q@`(TeEEH!{y1r18W<}N zO#D=-ZE73xkXica*q>-@$oFC4DN@JOF;?E6_!-j8bTcE}^O-IUO+&kX=+n=Xh=~}x ze?a^!sc-5VyMI7O4}`Cjj;5or^DV+x$yhVi*!2|QtEHZ)XDEwn>$6ry zm=VExit+1Yq#0@KdW!IMGTaO|c0Ysgdg+Aq*D}}M8?pZCV(k6~<2OrJ)7AL;7yS0$ zBHc}QWBIKazfB68!p8Dj6TVprnL=i4jj;c2mn~+CvHaJJzYV)Nm5tpmA$+?`!`ouz zF$v#+{o@Sd>v!<;yIZE1DN*pfGSy5q{{FEazf0ztxdA^n)88*M%}itWUkLA!IcAQb z>@L~|igSo*>arEAh`^ql7XR2rIaaUx;gwWWwY6AEWbSCzm|z+qB)M|Z|Qv_i_Ky~ z8E6ZCD~rq`LtaJ;e~0zUm8OZS-}iE{x!Ck~@DFkcz79nl{G%)}ON@V>;rEB1B-NxE z@}OFJCuOBsX*l1s{o|BmnJm-FJ+EKoZgaOGKbyrrE!UW9%bhC<`(>3cW{i^fgjH!Yb_&^2b=XsaYaRd_EZqH#3y4zR%HbZf4;dPktF2-@;J7nd=K%-US1OAc;pc0hvY2M4z^J8?qCb26J>dqeqd z^5@w6I+%Z8{j%BN$C+I?&$!CL9nF1^pI_+UPUc>@*I(D$@^m)$$UQ!m9yy=FMBk}`TxSJV=^AN6|YFS?gHbLTtJZd(-9{4tG z_IZMA`b3ikjz;naS-7W}jX4u#1T5UkP(J}>Gw^=leeYwQ$9aW+{_4Yh&5Jnk@Xud; zxW9P`-#B~zO8fzs`@SId{FU%v^PD{A%O=?J3^C8*8%j9=3n!abaNf)Ho~;k&)>!Xd z?8-mXtjC-s%fZ9U4UjM+50fqLaFdETCwZAHJi<`Ep8QP~9%)8{o0B|F79M35%VMA3 z$-<)zoA90 z;^1s^EjV4t%VnR>Ohf$$<)RCh!Vh_W?2ahbxcb>7xEI4N5N!v! z|MMY>kMDxEgB*XQ1o$C~-)*F5ZOG?GFdpEWpl?vFzXvSj{lPJ;Z$Q2ur=f}+SCx3! zZm@%3A*+vXlHxn0MIpmKeYj})Skbr?(S&iLi^hwlK~KOK$o+Rq6>R~11T!H2pWH~a zX$0x0UjTXt@Lg0qI|T=U;+g7)&_94(c1?ud0el@a2RaBk$HEVt1mIED&d@&q9%aS* zpq)p-9SxfV{|LC(z+MJ_4%}6+HQ>*JyApOP{8QjghE0cm2HaV&HDRxXT@E`9wwCCW zY*Fxk>5N>_-07mz7K--BK=?3(4@EsQ5w;p(S0ZdJ!mbwWzeaR6^dU^U1mSZLz7F9_ zP}iH$K35^ljcAKyqCMA(_PJhk!VQ2oiq6?CdSM;WjCly#hOi9?+k&twKyM=2@Fs+> zK=>wvUx4twbwo+@FqNHL=oN*XO+#OQTJ4fpHw zlY<8WI66<#?u4>z!Mr`p^RL`_QRT~v z8yy$z&x;qrp?-~=JTy1wfkXVzjXq=l-#NDr>4k1IU55Qtco?78jiwXrCm!p@{u0X4 zvd@I+Ls?utQ}SHVeb@`maN*M5AI~e~&kAL4^YRqz1rFn~;Le97{lAjU&C7=n=Fk4l zKEyItj^JZ~cjO@YOK-R=JMFaKy@~FN#HZEjzt@lH7{>BY-gimRy6{&}Dk z(xIETybQ~)etcHpI7d3iF{k3w<9laiof>6NyOjd1H1`o>vgbc3nJ~G@d2Wn%8p)x z^?#ZVa4yR5clZEz0RR6LM^}utDeHiggh3n579o!tN^Ko*-!7Z@bPLoG% zeb64Qu$wbLD92~pr@c_$*}V?#Eb;h#;L4wX8Fm{f(FFL0<>`gph>3E_(eI7bfcCY%#JO_`(?owZ0rp=aj zB32@uC%L-Q>@Ot5Qm&<<|Rs=~>!s4U-reno2NM;hh68pyttQtnkFjxOv zNJe&+S&sf3$Yl6Z!Oz7nh16v`xr_Pv?YA7O`SJ3x ztKa320UapSTz;#tni?!|4qlCw)eu?a=wA)_%;r9iuI&$Nv0Ibi^AlTm9d>iN`~1!p zz7DIjZZZn*6SQcfzSo0a5Im_q%>Hx(9XA9(3>qucoe=FYeQu3Cg zw+*YXT5_p-KW;`>tM88o-0z^?BVQk0{qCX#x{6`|)*_v-?!X$dNE^|{Sa&wYTC8zL z=$4CzZn^eYvta$Yyt8PjQlfRRhOJW!wjgXt*fLlX7r^=$>r1?Q+O9a($LGUe1#8#F zSRWU``WS1~NHy3PSgaW%Sd(Toz%umJE8h^;mUZ%biV`q8p=gKX{>Kx z(;SCCELVPE(d9f5RZh~WfxPO%hVzaPw>0Wq36}M$2bc9>9-LD$Z??;5ynB;iXQE6@ z$8fr<0dIn3xx%o@C~E`Q4zP)^+mY^W*!{5Y!hQ}LmVs`VE_B26=|Otty$CjMd`O?+ zEF+)uwJ1B`{(#59X2Q;aWfn6=9Us<#YT3mh(iO`=V&rJ1Y|EXu{iD(CXtuIKR!81^knbgAeM> z9u|Ki{Iyf8zZ?8Rx?BF5;z(0Y>nLS_@%@irTUb0TtK@4Yj3tJ2^agvB1tyHCs9f${ z<54b{Fwk&*y3|k(m~cnHUmJgz@L`}qAMaOY4|u!~Uz#w_yEz9W{t&=VnYRt)ehCi; zyxSZwlmR9@5%4CxTa)80oDF!j-l)eiEIbczvNyvcuNd){0iLS!^gsZw06a!hl`_MO zABDzC!HM{=K0aL9!YMv4Fyp5nek{%>DNjr|3vhSvt8jiycox%}(S~~63FiW?>(%!t zH%xdj;C9+e$fAvQZAV3R|CJ2SKFhE zFyZR}pU^W(d11m^05|hGct@vO_;$d?E9Oy-nDOM}DdAQ0C`U~A5x~22uTrL%@VkKD z(l3#;$r~C z*Kx2f<3qR*;C?zv?+DTp=6U)Twm+37%=7&%&HVH}yi0c}<&v4c4C1rBW!|&)d6p*3 z`3+@~8D95DGA887BE9Bc+1^u@*;N>{aq--+r z+X248++?WprfhKwm&P}N`fWHrW_&sP{?Xl^p8@uyvQ|;bYH@zhP76p)t*PZ5Tu>q! z(bpV)A*rU-w4aSH-A)TbUs^3qcW@D@t+n-YhhG%ByQ=Fc2N#zbT0^%0=6s-?LT8Jf zuW1f0DHXJWUg%)x|Ims`-gH~Ovfy_ws;e!G{Gs(riz#IaEWHX+KnrMF2UnDWT2SwC z_|>GimQnK9Tm0%$NDFBt^nXjgChivL;jMBo^iz9-yg3f8E&aR!9`9SQ_;n!<)7K+k zyrox9;=Oopf3pydl`dWvk8%++Q~WVN^2OSLfS*I2Ih zuJ#ff+(fSOuJTF(w)C3HDsPpyz{NM0HQpL;or7EAK9aRw1mmHl-$pJ1|4F8WLBE~P zZ*ZA|JNWzvH#xYY&u>6I4>o-#XuQ1CbanCFWr0~>hC4VBCl(9MZinC7=TG@_x|A&3*XK8)449>#Bqz*IhB7D?9w?{HX+zl=3lEZC&98=fJS;p|el|awPc4l4 z43XaqehyBSGvC)v@A;Xj?%;`X)EqVOz_;lq$@}JgL%nAfPL~hN z2j)o!PnIL*h@lQJ8$U%3o5QB7qc;QkA-^=#y=CKZwrD;w)L~}fZ28c9=zqUBA8x1F z_+FM%e$PVqYxDJ4@mtN-v*1m-=`4Kok+b0WI{z$qs!lx%=Jzr>zo*PohO#~OdCihH z%o~RKTr8X;uR|X80te?p?(`Ki)WH|v{+^f24;DuI&V~H#o2Jy%u)Qyox6E7SN{2sB zUNx_ptqz_KeVDJAZh&q3ERg4LKM?m~EWA*jG0zz8d02RnJZqjcSaJsCyBPAikD6Ef z_l^DI5+Q%b=MKJ9$p6vF;-h>k8@`zboVpy+f%-&%&!@r|wkhGPLlOavSa= zs_EdXWxL*@cUTzo*2tZDr#|W6wQ?8a|EP!1roTpZKqFC_gRjMXMz<>`F19|`$yVK} zpJRSv;p^oly-80y_y*acTlAgyklu}Qy;NV*!&v>mKbntCHT+HQv7i2Wn>oymE zx2)53O5J(3{=0BD(|B*6ga09!UZzLgaW?*bnc_|JsPoRkI3f2Yd(`b`%m1K^^hSDh z9lS?|d&52IWVG=Q$uMu2N4=63eprTjLp|yr zXP@sglBt<`or9m1X^@A#+riJtbe*o0&9miw5&BxkY0L~CM*c6$cpb0vFtJ5E;8$_l zy5yG#7JK`0o>fpB^AOEzxIyQ`dM^52)`-BjF5Avkd&7}_hK&qK)X7Qvj{zIR? z*27%i=63`q)LZ5C)DZr}=hwT(!Jqm3b=Wcq@Q?ZY<%^O-_&E6YH_EoO5dKOkm$)aCb^&u^OI@Wqfn07v#g`3<<=E14NXLpa9g&spfoSHS!%Kg(DL7c?iqZ_(bt zg}^KRlf3BY6$STj4YP7^NUxaB4?_J`wtb76l{oXh%e8k2L;j;D-18^{?&!v5?3fV0 zocRX)AOCdt6@31tr(AngG+)Y>(!jOP`96P4E0?~q`5HWr)!p-}fs^0j=FqT^ejT6x zp;dYa*ELP?8|C0e;2v*gGTifQY|7#HgR4(da|N`a6?f0SnYj);#+w}6%8>tPzf0fN zRK)LbN54I|&0Cm z;9T>qd@G$@`!4YLcc^#Gw#Pzq96LH|9K6W<6Fk0i-Sb;wt^yDIMpvGT!F^uXoObjs zHBUieXrrs&GM_)~b=O|Y&8PCIjBxNua8$e}BOLw9&1&fNEA86z3UE-ImU^zfYs_u| z7qNSvt}$istLoZoouPa|O9x+TYT@^->o4mK`K_ut_(t<8gJTu6^z?pMfX4lgsZu^BOd^B)j%_&^#y)N~Xhq2;3>}ONNVo-0T4l z{-^Hw>^BFYZ{=e5{0=}`ppaSa+UqG(7r)nB`#)c-}O@ubX?H zUiA4}x4ZmaGf&8qQqGO9??O7IfjQ~wd&ora+wbc4fzKazugmX4NLSP|y5%qjq^ti|rXYSdy83D1yeu!c_VJYbv!xyV zf=YR>FI;<;)Cc4NU*DLeS6a!RM_scPE~^h?XOwzzEnH3?f(*x(ZagWkKY$;Sx*QRY z`BDY_4jS~Sm(ju%mGW*cy6@HbdO2il`nvY6qG#lcT<7|4HT_sVmZI+aUPFJBAEV%! zdO}XfCKq2vf019L$N^28M7wy$CWz_a=pKtdgIUIx#JNeXJ0(rJjp# zpp@S$it^g{hPo6oM4t=_;YP~)#7aB3vChYhDkm|1+4*o2CBNp0{vm!-CBOT4mtQj_ zKWw5)-&}crSgM0t_;9+*zonAjdxuKYce{OCSw<^642T>9RAd-QeqeKb{4<&=x>tKX{@^jzk;6Y0M=SB`5>_7kO9Q`56^X1>Z{teso(CL(m zzdtA`>pgEmPR67^hIh-Eosl~`_m2@)tJxpugqqC$rZT`JxgAF7)~hlBXCw zGqclX=UNUf5>Qf zI`vQHO3KuX+&;P8NcImnQH6<$>xt{u2Th%qn=xxndQ$eJjL8}3|Dgv5=J4`ONWXA) zq+!Du6UBc%1A7hWmK5pPr+;Gdn4~@lLk1=f?3of7)+c%B?ChL$|1R?1uLNgMa0hxy z+JsI?vorhj=#npvy9_;V;`G`1;-7u>_&KwVz63olWp@R7-iYW6(DTMcUw_^s4}r3q(d#@L|hSn7A$9j=oduHm^fWh=Hz4%NI(OppAE&uO`bwxc|&LB6NZ*Ki&9+N zSzzACgv_*D^pfbjG5+<)8PavIr-Hx@xqMR zet6H!jF|(nb7r_~`pvpA^aLntZ#;A9&-bkLB2gIIi94*>LZpEG$fK2^bc#W$f<%dwI?J4ZMd>ywos z3H;+1kpTiNx-t-&bSL6NhSms@Sp|&B5f9qU#nXV`3K^0%NbVa-8~UNTh!OD+Z@Dmza?tR{TeF1hS$gxodt9YL?6RZ3D z40n~b7Y1I|*|9yadZVG_33r-k$dMIttx_)|XD_%s@qQY#N@C~b0NRlp zu;lt_k9N#!xVuEN18q+8Y*&;WZA&f#YO;$q+)3M=4-Mk9pNe!j7Ml{^p`nC9w0W(& zl{~rQJaE>vhn76FS<6I7roi?r22H+rZj|8XF2!f)W89tPikMoPxiaMLdxgp15JX>$ zHU-;Rh+K4a;_Xck(edQku2=8(5OP6MGu(L4*@EYDep}p35YSwoU?nstxjGtcL5@0d z4O6pFXr?s}eb(KIx{&jQW)u1aO$j^$N}W)n30LX7;k9N-8&|5Cb)#>3wIOI1yaUmu zO;j5W#WUdvcW5ZBzYqQ40_>V4A^Zv0%|RqJdqf+a>~Ob^8Z)9zY$Xd70-YnUc?~t0 zt%c=DH?`rkfhEV|b6tMN93Ka6&3Kd{2qV`S4Yf-1h6{2c@+L#>7E$9QPYGyFm9cK# ze{x;?nx`!DT3HTEaukv)f|@3%HMV98^eMd4JPG+4&yhC{k_+q%`YNTb@|ufL9-g8d zoM1UGqvOeKMu{e#s&QwMhSGh!rJI~OG)ECnjm0$NY^3?DhjsZ+?ni3bA%|sXc-tXO zC-j@;sTeEp-cmD8ZSGpYP%4qyWTOo=SA?cjB=nu_^45=sL5o}vAEP~@4R>;>{eZWt z@n&hByoF|SlJ$|>Xg~TcPc?b7Ii=Zn0>EA2wRk_sT}MN$AUx^)D?@ICc(fM{r5>pr ztJ@5W(+Cd@Z|*o)8GUVt4cUS+@HUk8MM4bg^5lcs+(V;y{xoNTF`68+(Mt?H-SQNlx_7CSiQ<4o<_Y z-{X0WZB2|f2d1OVK#!VO-^{VMn(#D&C+v9*xg?`aanOx6$??!uhxQK*wXjEwNU zyHFb#Pb@;Suc`I*M*cLsxsw`bxJyXRYI0+eL!MfJc(O`u#k?JZhMH~v%v22eW{AU` zIBNgm2?@2jy@_|F4*Gd$wjhq&OJATK25r0u_G8!>w0meMEpZ0pWHaJT)UHIJHTUtDL8# zp`k{xXhV&7W#ip8t*)%_OjDK4|SrH4K>+RLL4;)QR@~p;q$ch7{*Z=YWAX`gwJ0YYWvS? z{)P7@G`w{oG&|6?G%pRcF8|3z$(xIw#&?H?5~$=Je-nNC7WCoFRP=>rc&Ab37m>cf zbnzuyxSQO)CBE5cBXSDOMcY%NiCiBmQP<6|3$h_end6g{69wT0{f8mc{zF4a)EW`r zE9AhYv?~p@M^Pe&8t%ziKy8yW)zB{sHMM^J^H$B!EJy$4DFrzdUTcGQ7|)hkY`zTo z{rdwgCM6EWqwTdi=EnhD#!wnLG~~{THsn5}M9mbubCeb(w+l5aZbtdjTi^yL#8JYO zT!TD`r1m&U9#CWVA+#~I8LmZIo*M9EmfFPX;yXd@&y>t+kM_S~hP|DMQUR3yp%n2r zygQc^veL9wTVcJBj`4D)m4*n*N6lB%wia#3J^W|pT|Ac!=sVQfHLM-h9>IIb8)GY@ zO({JPnn=(_HzQvfa{4yLGuRgNSB7;%{^Y=;l*9Ihu?X?p>zq&668s(dC}%V zgyl7qe%=lG`I(!7@DmXmd#nKFX~FlF65TY^1V}>-Q8eZ8j`4Jq5`sK6A*WPmC`CYX z0OjwT>U-t6EWmhXsg{WG4VH#l_h=~1KuHgpX0>rMTOsQY|EXOxG&~s$4Yh3XHVPU=ngV#w@)}B^ zQ{tG$pf81nL6o=-4Nm}9qObgh_ky=Ov`0UwfOnOg6rs`fcn?q(au5GwCdO5?HO)nM zM%4684Go78&zpH^D8)&0Qzz@5M7z9%F@dHg+L?x$<7s$n3r`Jcj^H^{f+ag1>pqla z9`bq^bvuUd0yWoAyV1llKC1t>x&_u$L7T3?yG-t*f5&thg=dC((=3bX`;X!ooXZSr zYHxc>L>Zq!xk6JYVtvbl_j7eOv=7>soZd9#AmVLZ+fi<6JXnCX+|U+%Bo$*j+L#iT zRfUEUh?FuqjsTwSlB46=2%cq4%#ZMHEbEAVf@k_1 z%17x4N?=ecC?(*@y&4*7-lZW2T4){!`Ur2V2@N?8Xvit^lEHYLVc~jFWnu_5GtR&} zNe;`0O16;u zkT+;NT*roQiNky&s9)6*c;D-yy`uUbZ|yAy`n=VioMX4(c~nJx$>l@yQB=RPA^HC| zi~T+PDBc-LeZ7r-a}(;e4exda@_D;3>Yjq}syy}zkmr@NL~cY`?m}BrYauxqd77Sx zXUE%*#v$%i;EYdFS+v;^!L6alC)y-{c4(-rIj^}G?N2E$o|IFY7dhl8 zrLwOI>W(;Spri!nGPFsNahNBeEiS{DK+T9R;9Z(BBQHe@lXIJDggp*T~W%4lH|03?`RE#+RE^!VQLfKF+-lP#4N~DrourumIL(LX6 zV}WxBmYjOz=Iny{wLo6okq5aos&_*FL|-DOH%-h0d=K*U0ULAnKP6&m9%^CT@PBzR zjFG5YUPI0h z{|&g5sG}4eZz7$8uuSwhN~J&D3UvYg^AWt4==U^~IHd+WYVo6{QEHE)2D%pzM@}Jw z_@|H`x&BV!9jS+MQ{px>33$c}@&12|_nEg}(NF{2QR@|QU*|P5CfSfnP}UL{%UcEr za*Yux`l`8Wzh3RohO!`YnZN) zHNw@GjUTS_WS;+3w(tn0{!Y$Ft$fr7rF>?Ic%Od{{Dh;F`jx*!X~EA2c(hVqGizz# zF-m>Vbu2#k@5UZj+*2lP+NNKmJ@DJ);KiDNUl#{2(RlnglehU_tcmz>m1yBhv?qQw z9sV-ytX-5RPL}><+6_Ohk}bSKyW>|1ur1F@?T8;&To%4uJK*<)gRjsy{P@MO=~ruG zZK7NO*!vA$XM@)AYI&RuTKGn->DBZGS{To3lUDbtd)(Q#`ES;U7x6f{TX>7s z@M?J60kGxWs#U$J9!~=-yiKckRXmOm7QWesTec1J+pfRi#~lw#?^Zp9A5U5=e4C!e zkE61MZ`WV(v1RE())+D!hLu=QM2&<+QMt$akg&Z2ehTv(i`gF-9F4O zfQ^69$3K`EmS>L^^@@78JNO|j;uZ0dER6PlL<@QaJ$`L%`SxmIudp}U!H;PnuaL+4 z94-CFHO7na_?5QsKJ{Sub+Pb%ALg#4ZI36kf>*&S?cgW1yjR}a(JjpHpqBN@dc42g z=KqwI^U8VL)wS?5dY*Tl$JLvq|E!kwN_#mDeojkyrM!L)e%^;UOR)LBfEqrcS6CSJ zeM$G=#}g_`|7Cp`znu<#RUg8SvjCglYx)3woB>$)b={30Pq{4ohTe}KPaZA&rVqEa z{R#cyE&Uun-dASJ^EPfzJf_@jw)wxSpW?@z0t+9~qxf-G&cg5OXZUe9(bE4w-@)%D z2Y;xC@Z;*i#vj&?@#D#mg+EgMxN~6PBR>3!OaHOHhaXQhZ2TwsK7Ksqvhb(+A%0w4 zTlh2m0KdMD-cg?(SAsVFm=AO3(ZZkWyZG_+$HHIu<>hS0!vFNk%UP9$kLzLlxZ1Js zmp;75rT@w=Z@NqWFFk_a=Pv$hzx?A}`fvR5b9d0v`_?Z%cQh>goll=TSr-1@hq+^7 z;UE0+^OVItzaRB={BCvd34IN}?iNP>I;k(?$6Y7;{C?I~@#Biu!l(2V{J2wW>Hn%P z;>VpU3!l~(@cSOKAPfKI!*64SYV$v%-{Uve!pP5f-{?2WU4Bbnz3=op<%-tAG2XZO zt@32l(ktNoOaG-j8L)6c?<@UEc^YcrLO#rurOmIf_q0B(A9n^#Rtb2lRlh18nJ+^7iX~<*AH?OMCltpH6jf86T!T4EsGP<9X`& z`Y}RywXQx3roN15{8)`WEB=rM5l|AamstkG-uIf>L7k7~@T-~F-dafoc904nTf4RiLwLI$o z;;xfzpV}VvzjMdP;@9=44~?rEz!+a*J?ekss?y@u_o!d%78{TL(ZHiVIG%Re`Ze$- z=md?1$LJX4$^-Puw$n!5NFAx0EsXR{y&*b8dE#XAYvB#j!O9bMn_o*WNt2X2y%uie z4bTC~Q+Zq74qmd3@b#0}{5yEF1N|f+O#LL>?ZrX>^@;O%e>P8JZG4>foIa<~@MHSe zS#YwK>_x{LT<8=HH}jgE1!sHNXTfh_jE=@Xp(oCQD|wa9!l(X}X#5ghiL>H6dL7S# zkLz)LJTYv)j^0xIxZ`Zwvy->jyTn`T;4a=p-Xf28tl9YP-W=~jkGmGOeG|Od_>o)1 z!ackk{J4v4;Y4p1e&l4aa8EB6Kkmj_xR)PKeJ{3sdwHC%@l@L8*T-9_E0r8O7Vhh< z&=tDW!Tr2tx=c@682zKace!4!5`(m;jJi*(A+q}4Y zW7ALccpq3{M=#yu{Uqedvhh>B+s#fxZX8?w43GDTaQDf=(>&g1!_#{UPxp9#%Tfnt zdfUx*L(U%1LwRO;ysw12)s}v)ccocv1_8F|XM4OqWwL|kc)X8>TuwIqeD7-9Z$j=O zyB=EL@jjAge3;|O5^odkgE7!LuWO)pt|@fXRer}N?ml&ODE7*2OWW04m#_q zLw}u#6Em#5>I=|m7n=ZORf%xpf!_^yJ;W@ES8duKa6i$8NeJsJ+P$Y}^FHwRhV6w_ z^gTv53>FNHUoB= z=nKHw2L3kS%|m(rfwJ9? zHs1yOyI}7`x$XtdeXw_k&ff|5Zj|F5;5SmO@Brd*i=OF+XL6Y_W9~INYO2xn4A?0~ zn`axv4O5C+lk}~N;pRez`eLKg<{2%u%xIm_u)|=Nz^1~c7#%*rXon%dO*gu1jL|8R z;GY9`sL|dR8ikfHg;pMIH3RNMql2>GP6yp-a5DkVhP%{gpJccrjSgM}cP#8U*aff` z8LhMsc0T+gV28uzz+M779`<$A@eS-W^*8Drf&WX`?iP7L3~%(c%z*kLs&QX8^T{1?vH?5!2c%P1itpm3x>_hPX3VRy% zdsyhPm+r88;4cq10viWg3HDorHHBLa_Br^W_g-qjR)ie@`!m9z4<9$bDQ>e<$mc6= zZNnG>${Iu&*NS71)?m_g$QtW9Te#pJ+_Af+h^@IO?(OWwrycPWWL~nl_ z>C3?1!{{BK!~YS|b}@R_lkk5Ee+8rWz6k&G@E;J}Ro!UTUicq@eN9xG7=5>)eWUy8Ax$&TJO!Nga2vxuD*A9|xNjk>0o)Ja9)|4%|7VCh2LHcczk^kTwKh8G zC-_gmej{epMF?GEwEbG#F>tlfrdJt_T?M?AuvfyaHv0SuxXX=ZUk?8j#+<5%_xn18 ztutEhTDaF6?S2j18;p7RZlf5_wds1m8;#bz(U_;_B9B`Ub{pQATM)ht_&3458FoAD zX4p-zTa3=yin0HE(BElv_z%FpANEPuCybWchrHT@ z&MO#CUPjnUz zzQWk`E9{T3Kfs=X{RU&%KQWel0i3Tfmi>e=?0fh>hx-Y}vd_?;K85`l_6W+>2J7wu z_^!pMPW4n{(e0W-Kpb6NOP)cx-_YdU&|bw6UQG3oqQEHxe-YpmhApV(&bBCfIfR|3 z+P*B@Qot#LxYDpCVN0m)sSljGh-;wQI2K{mRl8S%TLZQhY;D*&u=QXgur<}Js-k*v zGo)#b*8qiT+5~i4Ag-|*iBql87h$~t_d<9d_i2-^cTL3Lku(6|u&Zm8ok0;?lL-Tx@wP1rQT28{lHT~ z>LvZMg+1+hSnP?*QMmLkLO3}Yc-l!WCF=ByPD_q8a;;J)D{<26+H~a3Ax9)75U976 zr(mSVwB)>H-aO%8KD6ZGW_pH^tN1&teZqXmnLx{U(l{QZ+1cCT@RXMIARV6Eg)Zww zT%M-V&p2`pv8*f)%N2$*EZQH|B`m|=ZKrxTF^NvgGBIzq74r$(jHkq5UgY8+R|@NR zuJ$9xDAPwjFXqEj*02qjSANfqoJ`TUVZP_eFTZ@kKEpiDl}~c~x$7R?U&!IkyC1@G zu>Q2?Di2S@S$;}g{8=Aj`sjZ2pVouihIyaiZ|Lu=H@Q&hQnG_?sLPptO2yD6M*}(A zLqE$wOOCxz*K^pHpH8)S&Bl{NnAn*U+ zxWs-L>cVDxXdgsA?BlEOyvSuudg~ilntbkwsDFN8OwVV-ax#5>J zLtMHW(T*X^K0;cQ_F))LxJNg#+@m25UD6NrUUxtp;)Yp#j=|A*pWuCgnF!8 zIk_0la?)kl$T?0wpEX@dFwo@~L^rfyA0f9e^9cJb>&5zn@x&)5J-LAB51%zT0}|1u z)87H%!h6ck)@-PvK|!*>Kq(NRRWa=sp+S7yqjNv7BLD zNQ?cB>6u43eE7T7kmN`29&)&bhG{bp(E3_TUM!Jiz zFU>MVuNk80|5ZD193VcWy(oRhb;81$Xmgx5Zi%2$Fgy!+v?RuBd|N`p^8kL&ct<03 z9Y2N$-r34?On&QlpHOHDoR9e;Xix{&e2jnH>e_i7$3)&+G#B|FuM=XQ?NYBgbz_$W z4f0TpLpl1Uo{bX?Q7><3I7j8TiDvH-uX_sKSOO4 zC_fGF0OS}*Qw7ho1fCJ^tIN;CVtnFVR^wwZkHPZ{%~HHa33ztoL*dyqCBndCspLrH zz9#VvLa3LXcY5+Z-VZ=a&~DUqO2ad%p?KaLBNoSl!@W2-Tk&jomXx1q1{&W_wJ}_G z{DQXkrl5b<1C3JP3P*eMZqehVkqy2DHSnG8S`PgQ`E42t4$PcOO9B(;KPexrXoghN~4Vse~m>=TVa&8t|1yZ+l(AVR@36A_Zx2hJ@brx`L ztAyu^{C1+hJ%VuR?5E!2uaO_;Wd1#-pv$vR8jkfmM-2_fT=K1Oo*8X;H+yJq!SnpP z23^P*>KV+>lttO|GdzcGiar@_$Qu=Hc-Bb$;?+}aD8FCHXm9TOP>(#%ZWrTyxU2%k zLiA7SF6I5WG&drgyhG$a;+<{OBY9UtNO`2=J?oBs5#SM*I`(PSmB2IaW&OoU;hO-M zhIb&4&+6+`AN1Fx)@T1eM_p*fqpxoPZJNi?7rW-^`wUM+S!j5#2YKbrWxk0TC%8@` zpCJv31!M5;dlDlBq#(E%3gb?pqPXp=IPS(`lfD=9L+W1rcMRv`G}L`a^9tU)yBQQHm#TOc*5O;0%W!asv zZ}Fv0Xo5EtZ|3%#ys3YOuZqWR`UDG8%Pl1ds6ChF+v515U<8ZBm+h9a82JX+-MJ&k zgJv7v;&jmN6}++ER0P*mP*$!sQb1=TzG%^gI}J2Vg3+sgHS8+1$7~CuHdh4XY@*p5 zRcCV79gMfTkbWfocpDuJccDU4BHsFz&I&`U%bhh0oW9{r4r^*yKe_ZuB5v|PyD}JC z7^54$`rHLO52M_<3~xi2hB{~UJr{-ze-@+N-Gv~3g%NO%{*KFCf?n8B{a@{!3A|NR z^~cYB1Mf0C-eXWerpprqQ-K_a4D$k_U|5Kdv%CjFP)vfBgvwJ*bHS_JwJv%THBlT&n!^RK7Nt&rUm$)*>BtCTAP_c zXRe>YyzS{V;O0!V+wXX5(DT4-3svB4x3AQpZKltM(>5a?o4r$yhSJH5a=7lxkUfQ` zh_fEGn;AXEqJLYiQLW=^H!}rP(uTU*pScE&_&%TOLeGpnIcIgZww!A>tz^wiRChZ$ zyipn9e@C0ypK3QVb?s<#wV-zE&3QJ8zBB7}x2Mp}?6;4wI}$ZBu+f_rR@UYEXlxct zGgq6PXxnXC1KwsiueF<_X0wi&`SqkqMpX2pn~~bx%*gcHJrZ2|*Qobq4si5dyBUeR zc5`N_?zV*M+sqZeo9FX*1e#E}(Blda| zlfK(_#IDCl-A#|Lq5P))6G!aVp7F_z*a;*VXPmt~Tz~ly`_`-E{~NKt8Tu&G!;#!o);^pT?`*X8c^@v*&)ziujtt|KN++{L1%NZ5^H zeXGU|H2=bG6g~3DBX*}0A>9W|*!_Z|cd=iNsTwnWH)=bE3>TBeRE^!8qJ-?;opM7a zjojrkIBDeM31>|jF%mbKI@jgLU1?S3j~RLP#9b>jYSihwR$$1aF+?sO`S((1R!t&k ze7D-#GY4djH(Y-v$&?f8zay5~tT{2$H%CsIOj?(%Key!9_;Ic7S2xc7NA%W#+&&zn z?Jz$7=!kK0#CV=10tU<%zrE>jj&97`uu#CH2VXcoZ@{q$#|bQzSmsKOj<5SbZ_4p@ zj?Ak>%-ylfz~m7-^9u1+#NT__8CTaXgU%81KYe&2lUhk!AR+@yC!-u?D+(@R3>f$HCU6k zAzI-rj&^|{q^lv+-k*R`bW{69v0g^sD&bCKiR4xmgOB+`H9NBvMX3)}x)Wt;`^(v5 zXWIvJyF4^HGnCxPdh68FFSp zJf|Hiwi32MiC%-^Ri|yf`j>vJ?JpxaI)XnS)@JZ0t5fLd>Z0it1=ZP9reJ#Zh>D7u z>Dlu0`ybK2CGNmcO${fn&bA`H?uT_T+kla$lgaY!evkf^{^?uuERJ5&(=`>;wjjPw zWR5c%hS6Pp*t16TAssRw`t%8Z$WE_WP?N5jo=(@KD>A;D#9PM?Zs9&LsGoKuu3-A| zlDryaVzk|#>S^1NK6yeWn()T0=ylu>?Th}Mwm;1{484Y&XmnPimo`Z?itdTdOU;`) zYIEeOSGCt;=8EUGJkxXj+>Jl#@zSQ;BM;pB+zV@REAP$aa=tWvvYW4`*=YHD?|2>u z+~1Lv9e#{`c|IJD5BiR_5C3X*#I-Z@b55PpaZ>M!g&hkPr$2M+v&)tZOcgCRaYDQ$ z*Zr_A+JG7VY#c$5jDHZmt_jDTIaIGa^P&51-;&F1$*mmrNN(CU=+0re+{%r)XL4(E zxy^6oR!+H+U& zT8#|ullTdnnZ$_cYZxKF$BXx0*p0?*#AodnwP~z^M;lfxQ;bflO{tM|wWHLQ)#$>P zF!DDNtC7pKlQzn)*_Kwb8)00F3$w}}e18zEnN#SP4i7!kRE&t{F&8by0t?W47>S$8OX{td=Hy%QT~Em5?v(kjMmLS|-a z0go%{+68NcWwhjH!E=zO?Y`DPosp?^f*C%vau`Y*Ug3K!VYH?)j+U{jjQ(qs>ft3Ao@#a6?J(5lQVq3Z$!VmGVit3nR6R|@brbbZhh~p znexZeMr>-guKC7w7r)zf>5E_JwK!LE?Ut7}oc4NdKyK5j_7AqZ;;2LSX%jWo72<2aM(-AXmZyGIsOyVN(|K+adTAV4TD%^9;~I)Lpo7<-_-*L^&cqq&HMXw! zN_h0)5Z?fg*S>gfD7_*4 z`a+1C(?T*SXJQ>4oT{)vWjnV4v0EwzS3PqgXEQ>Ea^rjU2^1 z;$n+b@v)Tk-YhOgXJdwnx1f*j0OAxnm<>q$9k7{@)F(4zeSw*?r1wIO@lVC?Q`Ywc z@eknFhe-SxbTH$TxH-5-=(k1i_^g!v3vdN@K=GH6S*?fw(jLX2}wdhVJuT+!ef*qnPR=^?!CdhnQyO8Q0M&Vj!b{AL@q-)gv5 zuMGMegg$2XlD`nC#ugU8jt*L3C-lTJU3?*QEu6)_0_#sG{w6vYM^xO6b7!_q@hEig z-ALS9okM$XhF{B2=@-MJZ;H4XI+%4yd?32@4f^jvSu>u6lh)PZ)4;}17QY6+@lD0u(NkYeaUn8&#}gOx$&AI~8_`q0C-HRT_>L@o6uLe& z;^yf5)u3ln%9>5ieqMKWu0YEpdD6&#d2oWO(7_n2(z~LA?~~&9k!cnoaW~}XTP0ot zznN>rXTy`~lwf~_`glsOLO0(h#YNn&^tTdEgYLLNTpw(<2r<<}W)l|gMO~MKdb@)i zH%M8lhD&x{o-rEzGsWygui>}hZ6YpL6p80 z9<7kY_4sGzEAi>JCFooO_FY{1JIJ{%=-CxLeXp0k4?OxsiR-hMWDY&!eoqrsB_|oBogDJ&~^upm-!aTK|g=fNmB$aU1xJeJy^EwrIUC9?Lm( zydmC?cA5D`d?|ADJrq+-JZ2NO<5T-gLT`s|W>}Mc0sQ)}iGPWFvjvHN58X8l@&4#z z1|xAZaPQETUg&1F3+a{cn<>tIxaQ`ygzrn}mq0%#li;hVSId6+dr)?M(BVRO%z7z3 z&2DUR@vG?L_)*-Bdd&>iIxIkrc>KGD+7g{Lv&NO2kcW(HmHKVPAD zkB+0|DdvqsF9dO4WEPYpxIb;YDDXcL)R}}l*%uIrp!ta<- zdUtr7&-43N|F*8cak2CY=#EXr2Y~gm5D%bUv#f~wK{s}-_;mWmxdU+%=<_m(%oQ({ z6q+$sdJ((+q2i(NFM>zhi1r$TJjT$?oG$Le^klmcBXs?y#Um-} zxLHj6yGV~0@zL<#AFj97@VibcohC&OWD;D84$coqZx4U(pyyYSY5o%Fb0}-(*Za*PiweK=U(XmKa56Jr91XTxs{ zcX0!F%s(Jr3Ef!A;^Uw@Un1_vb!CiU@d)V7y@>Tmbsj<74c*M+BEAec3qx7GSC@sd zRg@hb>Ut5L4>AdVW7??yx@AvcNTvc9y9%l>!E|0RmB55 zFQMMg@QryEq`yR2J*31uz2Z3?aclUUXAXNXh$HctzEyBBohk4qm&S?3JJdr;cA z=HmX!57)-;;CFsZdVT&G+gp4RJZ6j%7xGvCXz_DgL8aLQU!o2{&o;>&E zPjOc;OR`w+?E`}jMq0-5gyeTt!&bx!(kC~MYr zaWVS$5Ap{f-#j+b@1v}qcH&#WdRK~{rL10g;#;AAGsr1Ijxpk;ABh~ZZ;Ko8*LVzK zuL$R2#e1TsIdsJLBF8ylG1|xTtO+iO=Z(emV4M|Cd>j1crx8B}HY>Y$FXZcmDDH<2 zdU}bUqO@KS;;~>o0ma{^?67QtuSdQa%cb81Hj}2f9)I<*7yktQo$1Re9X!s#OK%J|AAq>BY8 zkv}rXT!_rOvk84Jbn{-w(+T-M4!X5LH#6=^Z-sngONfb+8_f^xT811wN2NamkDkrq z7GT#u#0`+YFzB{7x|v~I`W$#3%O?0nc+7?=y$SRuf)3^A@M@5AB|L|Ox*mk*?jW-h zGSgp5%KiWz;{qwC3?A*TxD`aBd3SZKLHbM3S7Z}>HT>qtl71&`F{7@y0sYoA$Y}<@ znYX211;6oL#IvD49(3r44ra5I{=d-81S(zvzn-$<8||wgvlJdPj!Lhftl90wSHS;x z(B}^L^;VbO6n-<^ij8q$Y#s3*)FGU^&YU~#HKeEb_2@w+Zh;)Lxr*;V{((VeW6J7P zF8y-wYeAn5=+hzSGYfuWP{{KdJZ4%HH$n&Fd5FuQo9$cN67BSkO?XNpv)GFFgy+ja zhue^OevtVpbTduMGZVVrfa1C6ye71(9qrPyTY4$F9Uo-wL0RL*NMFP^uBC~~c!s)G zCjJ>b#vu_m0=qsZ?t-3XYZRwxi)&Hh7vXX3O56^aW*ik?4mQ)T_*Sra$HYs~^Xox= zbIQ6lC4CLJL(sW3I=e-d5DRKFI$eGF?xU=L4`_^5QGOu91pYfX(hK zo{#(sLK_dHjTZ+UCO~&RS)TjhHy4(8Gxfe3WL86W9aQ>X!LD(N{{}XTs`y}Jng>k$ z4rMP3^)5ud>)_Ihz-B-$054}w-nC)r9cY(P0>n2V(=}0XA=ieU?BX*h>pHMyYq+nP z-Cg<@sMmF2@e7o_IOy;$I=E&m{dwrd^0Dj_tl2#u>TN>3i-VpwA^*`(*Ld(xgZ$4U ze?!p!6zFC*Rpw>LbiG!*1#FgZ@j>tyvr4=g9hL_93!q;bcn${tHpo8+`L6%V|0CoW zjX-=VJcoz6u7}>Jd7}S{Gor%I)N5Oo(uZq9TlSzWRY8t%h+Jb=&d_^J92iUl(^kjxn3WMd)LU zC-Jk$G*X4QCpw(>)dcs4Zfq#&Z-BLL5Z{MPqhpARxesT8%>9w6t%CHX)b)1Ija&@)jh&?`^I8F}T|j2v?lihqs#qk{fR(Eps!#%tlxo&0tce>?ud%zt z>!6zLrOVy0N}M#^fhpbpnCe{PO9=|w(AiWAJ08ry8hvj`qztI2aUGPSu7w?(El z5aLeMYi4WlTIB1+D*i558wcg22No1+t04Ui=;rnlzllsUP>T;k<{*wdG1*=5a0S-c zsD{iHI^r&4PxB5`#-3&@R>q#@{i}?f2Q*s)N)+?Cn9ub&G8*I@l(DB7kC(Bh(Wi_( z&0qhec~>lBPxBluW4AtMuvp1kz13s5?#P~XvxEgvRJ~!f13yv)~ zmU856n6f;3*vX?phLDE5UzD*Y{Xj*?%5V7|$oIgfwFmaUzk90kHz)Mu(Hk}6Hq>o= z)wq$tgE#R@+}7i8>*u`xZ|o4g6E2uX=KX)ijGcVSM>v`0{eOpd`&at^4nF0qGk0)m zh7OuEL2qeBDtZ6kPwVp`-|qkJ9%w`J7BR3I^jeR?9MAujZ}u&;#q(P9h>w-f@4P08 zDyQ}+bkxw3@24|FS<+j_SI+lmBIj+&jpn$D;|YAfoZ||HHcLJ7c?m}j z(L|0dsc+R}_~7q?+(it9CL-@oeBYg;e6!(O#h5TNp=aTJ$mtK?4-Vt|);$U<_`Z_w zJyvr>T$JW}raq!|ZzS?w<=Bc@v|FR|S+hNw4AujqW3Vdx3Zuwl@svG|9Ku7%!+Jbc zr6)!wVLy8c^A)FIpHYdO(QxMFF2ok#;wKO7!h$D%Ke1 zVwE#3s*e5#tI8S7;Lc(;W;U~>bC}7#3hUEF%ocf$@>{+K@;#96fqW0-dm!Hf`5wsk oK)wg^J&^B#d=KP%Am0P|9?17Vz6bI>kne$f59E7bS9;+808#QLockAxisTripods(TRUE);} + void TransformFinish(TimeValue t) {if (ip) ip->LockAxisTripods(FALSE);} + void TransformCancel(TimeValue t) {if (ip) ip->LockAxisTripods(FALSE);} + + // From Modifier + ChannelMask ChannelsUsed() {return PART_GEOM|PART_TOPO|PART_SELECT|PART_SUBSEL_TYPE;} + ChannelMask ChannelsChanged() {return PART_GEOM;} + Class_ID InputType() {return defObjectClassID;} + Interval LocalValidity(TimeValue t); + void ModifyObject(TimeValue t, ModContext &mc, ObjectState *os, INode *node); + + // From ReferenceTarget/Maker + int NumRefs() {return 66;} + RefTargetHandle GetReference(int i); + void SetReference(int i, RefTargetHandle rtarg); + int NumSubs() {return 66;} + Animatable* SubAnim(int i); + TSTR SubAnimName(int i); + + IOResult Load(ILoad *iload); + IOResult Save(ISave *isave); + + RefResult NotifyRefChanged(Interval changeInt, RefTargetHandle hTarget, PartID& partID, RefMessage message); + + int DrawControlPoints(TimeValue t,ViewExp *vpt,GraphicsWindow *gw,Box3 box,BOOL ht=FALSE,INode *inode=NULL,ModContext *mc=NULL,int flags=0); + Matrix3 CompTM(TimeValue t,INode *inode,ModContext *mc); + void PlugControllers(TimeValue t); + + virtual int NumCtrlPts()=0; + virtual Control* GetPtCont(int i)=0; + virtual void SetPtCont(int i,Control *c)=0; + virtual Point3& GetPt(int i)=0; + virtual Point3 *GetPtPtr()=0; + virtual int GridWidth()=0; + virtual int GridIndex(int i,int j,int k)=0; + virtual Point3 GetControlPoint(TimeValue t, int i, int src=FALSE)=0; + }; + +#endif \ No newline at end of file diff --git a/Utils/GinExp/resource.h b/Utils/GinExp/resource.h new file mode 100644 index 000000000..ba30c4a5c --- /dev/null +++ b/Utils/GinExp/resource.h @@ -0,0 +1,68 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by asciiexp.rc +// +#define IDS_VERSION 1 +#define IDS_ASCIIEXP 1 +#define IDS_CATEGORY 2 +#define IDS_EXTENSION1 3 +#define IDS_LONGDESC 4 +#define IDS_SHORTDESC 5 +#define IDS_COPYRIGHT 6 +#define IDS_PROGRESS_MSG 7 +#define IDS_PROGRESS_MSG1 7 +#define IDS_VERSIONSTRING 8 +#define IDS_LIBDESCRIPTION 9 +#define IDS_PROGRESS_MSG2 10 +#define IDD_PANEL 101 +#define IDD_ABOUTBOX 102 +#define IDD_ASCIIEXPORT_DLG 103 +#define IDB_BITMAP1 108 +#define IDB_BITMAP2 110 +#define IDB_BITMAP3 113 +#define IDD_ENDDIALOG 115 +#define IDB_BITMAP4 116 +#define IDC_CLOSEBUTTON 1000 +#define IDC_MESHDATA 1002 +#define IDC_ANIMKEYS 1003 +#define IDC_MATERIAL 1004 +#define IDC_MESHANIM 1005 +#define IDC_NORMALS 1006 +#define IDC_SPRITEPAGE 1006 +#define IDC_TEXCOORDS 1007 +#define IDC_CAMERAANIM 1007 +#define IDC_OBJ_GEOM 1008 +#define IDC_SPRITEPAGE3 1008 +#define IDC_BONEDATA 1008 +#define IDC_OBJ_SHAPE 1009 +#define IDC_BONEANIM 1009 +#define IDC_OBJ_CAMLIGHT 1010 +#define IDC_OBJ_CAMERA 1010 +#define IDC_OBJ_HELPER 1011 +#define IDC_CAMLIGHTANIM 1013 +#define IDC_RADIO_USEKEYS 1016 +#define IDC_RADIO_SAMPLE 1017 +#define IDC_IKJOINTS 1018 +#define IDC_OBJ_LIGHT 1019 +#define IDC_VERTEXCOLORS 1020 +#define IDC_ANIMCHECK 1023 +#define IDC_ANIMTEXT 1024 +#define IDC_CONT_STEP 1155 +#define IDC_CONT_STEP_SPIN 1156 +#define IDC_MESH_STEP 1157 +#define IDC_MESH_STEP_SPIN 1158 +#define IDC_STATIC_FRAME 1161 +#define IDC_STATIC_FRAME_SPIN 1162 +#define IDC_PREC 1163 +#define IDC_PREC_SPIN 1164 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 118 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1025 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/source/player/player.cpp b/source/player/player.cpp index 281571a7b..9fd4e84fb 100644 --- a/source/player/player.cpp +++ b/source/player/player.cpp @@ -4,11 +4,39 @@ #include "system\global.h" #include "Game\Thing.h" +#include "Gfx\Skel.h" +#include "gfx\anim.h" #include "Player\Player.h" +// to be removed +#include "fileio\fileio.h" +#include "utils\utils.h" +#include "gfx\tpage.h" +#include "gfx\prim.h" + +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ +CPlayer::CPlayer() +{ + SetRenderObj(&Skel); + SetAnimateObj(&Skel); + +}; + +/*****************************************************************************/ +CPlayer::~CPlayer() +{ +} + /*****************************************************************************/ void CPlayer::init() { +sActor3dHdr *Hdr=Skel.Load(ACTORS_SPONGEBOB_A3D); + Skel.Init(Hdr); + TPLoadTex(ACTORS_SPONGEBOB_TEX); + Skel.setAnimDatabase(CAnimDB::GetPlayerAnimBank()); + } /*****************************************************************************/ @@ -17,14 +45,7 @@ void CPlayer::shutdown() } /*****************************************************************************/ -void CPlayer::think() +void CPlayer::Animate() { + Skel.Animate(this); } - -/*****************************************************************************/ -void CPlayer::render() -{ -} - -/*****************************************************************************/ - diff --git a/source/player/player.h b/source/player/player.h index 1d775cf74..2e66613d4 100644 --- a/source/player/player.h +++ b/source/player/player.h @@ -5,21 +5,26 @@ #ifndef __PLAYER_H__ #define __PLAYER_H__ +#include #include "Game/Thing.h" +#include "Gfx/Skel.h" /*****************************************************************************/ + class CPlayer : public CThing { public: CPlayer(); -virtual ~CPlayer(); + virtual ~CPlayer(); void init(); void shutdown(); - void think(); - void render(); + void Animate(); + protected: + + CSkel Skel; }; /*****************************************************************************/