public static Dictionary <int, CgfLoader> CreateBrushLstCgfLoaderMap( DirManager meshesDir, BrushLstLoader brushlst) { var cgfMap = new Dictionary <int, CgfLoader>(); foreach (var brushInfo in brushlst.brushInfoList) { if (!meshesDir.Exists(brushInfo.filename)) { Log.WriteLine("**Model not found: " + brushInfo.filename); continue; } using (var cgfStream = meshesDir.OpenFile(brushInfo.filename)) { var c = new CgfLoader(cgfStream); cgfMap.Add(brushInfo.brushInfoIndex, c); } } return(cgfMap); }
// hack: creates a new cgf at the specified time in ticks. // uses the controllers to modify the original transforms. // this loads exact keyframe values, curves are not interpolated. public CgfLoader CloneAtTime(int ticks) { // TODO - check loop type. // TODO - check ticks is not greater than global range - need to load timing chunk. // TODO - validate keyframe start times are ascending and within global range. // TODO - validate controller type. TCB3 for pos, scale, TCBQ for rot, others unexpected... // TODO - validate cga vs cgf... some doors have .cgf extension... CgfLoader clone; using (var stream = new MemoryStream(m_originalFileBytes)) clone = new CgfLoader(stream); //Debug.WriteLine("cga"); //Debug.WriteLine("-----------------------------------------------"); foreach (var node in clone.Nodes) { if (node.positionControllerId != -1) { var cd = clone.GetControllerData(node.positionControllerId, ControllerType.TCB3); for (int i = cd.Count - 1; i >= 0; i--) { if (ticks >= cd[i].StartTicks) { node.position.X = cd[i].Params[0]; node.position.Y = cd[i].Params[1]; node.position.Z = cd[i].Params[2]; break; } } } //Debug.WriteLine("node"); //Debug.WriteLine(""); if (node.rotationControllerId != -1) { // Note: collision meshes usually have simpler transforms, for example most have no rotation, // so bugs here have less chance of affecting geo data. // BUG: idraksha - idraksha_door\IDraksha5f_eventbridge01a.cga - rotated incorrectly, but has no collision data. var cd = clone.GetControllerData(node.rotationControllerId, ControllerType.TCBQ); if (cd.Count <= 1) { // BUG: doors in theo lab have nodes with broken rotations. // see librarydoor_04d controller=12 values: {0, 45 deg, 45 deg, 180 deg} // they appear in a 1-key set, so ignoring those for now. goto skipRot; } var rot = new Quaternion(node.rotQuat[0], node.rotQuat[1], node.rotQuat[2], node.rotQuat[3]); int curTime = 0; for (int i = 0; i < cd.Count; i++) { // params 4 through 8: t,c,b,ein,eout //if (cd[i].Params[4] != 0 || cd[i].Params[5] != 0 || cd[i].Params[6] != 0 || cd[i].Params[7] != 0 || cd[i].Params[8] != 0) // throw new InvalidOperationException("expected zeroes"); if (!(Math.Abs(cd[i].Params[0] - 1) < 0.00001 && Math.Abs(cd[i].Params[1]) < 0.00001 && Math.Abs(cd[i].Params[2]) < 0.00001 && Math.Abs(cd[i].Params[3]) < 0.00001)) { rot *= Quaternion.CreateFromAxisAngle( new Vector3(cd[i].Params[0], cd[i].Params[1], cd[i].Params[2]), cd[i].Params[3]); } // Check if ticks ends before the next frame, or past the end if ((i < cd.Count - 1 && ticks <= curTime + cd[i].StartTicks) || i == cd.Count - 1) { // stop and use this frame node.rotQuat[0] = rot.X; node.rotQuat[1] = rot.Y; node.rotQuat[2] = rot.Z; node.rotQuat[3] = rot.W; break; } } skipRot :; } if (node.scaleControllerId != -1) { var cd = clone.GetControllerData(node.scaleControllerId, ControllerType.TCB3); foreach (var d in cd) { if (d.Params[0] != 1 || d.Params[1] != 1 || d.Params[2] != 1) { node.scale.X *= d.Params[0]; node.scale.Y *= d.Params[1]; node.scale.Z *= d.Params[2]; } } } } return(clone); }