//====================================================================== //incoming() // //Return the incoming tangent to the curve at key1. The value returned //for the BEZ2 case is used when extrapolating a linear post behavior. //====================================================================== static float Incoming(EnvelopeKey key0, EnvelopeKey key1, EnvelopeKey keyn1) { float a, b, d, t, _in; switch (key1.Shape) { case KeyShape.ID_LINE: { d = key1.Value - key0.Value; if (keyn1 != null) { t = (key1.Time - key0.Time) / (keyn1.Time - key0.Time); _in = t * (keyn1.Value - key1.Value + d); } else _in = d; break; } case KeyShape.ID_TCB: { a = (1.0f - key1.Tension) * (1.0f - key1.Continuity) * (1.0f + key1.Bias); b = (1.0f - key1.Tension) * (1.0f + key1.Continuity) * (1.0f - key1.Bias); d = key1.Value - key0.Value; if (keyn1 != null) { t = (key1.Time - key0.Time) / (keyn1.Time - key0.Time); _in = t * (b * (keyn1.Value - key1.Value) + a * d); } else _in = a * d; break; } case KeyShape.ID_BEZI: case KeyShape.ID_HERM: { _in = key1.param[0]; if (keyn1 != null) _in *= (key1.Time - key0.Time) / (keyn1.Time - key0.Time); break; } case KeyShape.ID_BEZ2: { _in = key1.param[1] * (key1.Time - key0.Time); if (Math.Abs(key1.param[0]) > 1e-5f) _in /= key1.param[0]; else _in *= 1e5f; break; } case KeyShape.ID_STEP: default: { _in = 0.0f; break; } } return _in; }
//====================================================================== //bez2() // //Interpolate the value of a BEZ2 curve. //====================================================================== static float Bez2(EnvelopeKey key0, EnvelopeKey key1, float time) { float x, y, t, t0 = 0.0f, t1 = 1.0f; if (key0.Shape == KeyShape.ID_BEZ2) x = key0.Time + key0.param[2]; else x = key0.Time + (key1.Time - key0.Time) / 3.0f; t = Bez2_time(key0.Time, x, key1.Time + key1.param[0], key1.Time, time, ref t0, ref t1); if (key0.Shape == KeyShape.ID_BEZ2) y = key0.Value + key0.param[3]; else y = key0.Value + key0.param[1] / 3.0f; return Bezier(key0.Value, y, key1.param[1] + key1.Value, key1.Value, t); }
//====================================================================== // AddTextureVelocity() // // Add a triple of envelopes to simulate the old texture velocity // parameters. //====================================================================== static uint AddTextureVelocity(float[] pos, float[] vel, List<Envelope> elist) { Envelope env = null; for (var i = 0; i < 3; i++) { env = new Envelope(); var key0 = new EnvelopeKey(0, // time pos[i]); // value var key1 = new EnvelopeKey(1, // time pos[i] + vel[i] * 30.0f); // value key0.Shape = key1.Shape = KeyShape.ID_LINE; env.Index = (uint)(elist.Count + 1); // Q: shouldn't this be + 0? env.Type = 0x0301 + i; env.Name = "Position." + i; env.Keys.Add(key0); env.Keys.Add(key1); env.PreBehavior = EvalType.BEH_LINEAR; env.PostBehavior = EvalType.BEH_LINEAR; elist.Add(env); } return env.Index - 2; // Q: why the -2? }
public static Envelope ReadEnvelope(ChunkReader reader) { var env = new Envelope(); // allocate the Envelope structure env.Index = reader.ReadVariableLengthIndex(); // index EnvelopeKey lastKey = null; while (reader.BytesLeft > 0) { // process subchunks as they're encountered var id = reader.ReadID<EnvelopeType>(); var sz = reader.ReadUInt16(); sz += (ushort)(sz & 1); using (var subChunkReader = reader.GetSubChunk(sz)) { switch (id) { case EnvelopeType.ID_TYPE: { env.Type = subChunkReader.ReadUInt16(); break; } case EnvelopeType.ID_NAME: { env.Name = subChunkReader.ReadString(); break; } case EnvelopeType.ID_PRE: { env.PreBehavior = (EvalType)subChunkReader.ReadUInt16(); break; } case EnvelopeType.ID_POST: { env.PostBehavior = (EvalType)subChunkReader.ReadUInt16(); break; } case EnvelopeType.ID_KEY: { lastKey = new EnvelopeKey( subChunkReader.ReadSingle(), // time subChunkReader.ReadSingle() // value ); env.Keys.Add(lastKey); //TODO: not sort all the time env.Keys.Sort((Comparison<EnvelopeKey>)delegate(EnvelopeKey k1, EnvelopeKey k2) { return k1.Time > k2.Time ? 1 : k1.Time < k2.Time ? -1 : 0; }); break; } case EnvelopeType.ID_SPAN: { if (lastKey == null) // We should've encountered an ID_KEY before an ID_SPAN throw new Exception("Key not defined"); //TODO: make proper exception class lastKey.Shape = subChunkReader.ReadID<KeyShape>(); switch (lastKey.Shape) { case KeyShape.ID_TCB: { lastKey.Tension = subChunkReader.ReadSingle(); lastKey.Continuity = subChunkReader.ReadSingle(); lastKey.Bias = subChunkReader.ReadSingle(); break; } case KeyShape.ID_BEZI: case KeyShape.ID_HERM: case KeyShape.ID_BEZ2: { Array.Clear(lastKey.param, 0, lastKey.param.Length); int i = 0; while (i < 4 && subChunkReader.BytesLeft > 0) { lastKey.param[i] = subChunkReader.ReadSingle(); i++; } break; } case KeyShape.ID_LINE: break; default: Console.WriteLine("Unknown envelope span shape type " + reader.GetIDString((uint)lastKey.Shape)); break; } break; } case EnvelopeType.ID_CHAN: { var plug = new LightwavePlugin(); plug.name = subChunkReader.ReadString(); plug.flags = subChunkReader.ReadUInt16(); plug.data = subChunkReader.ReadBytes((uint)reader.BytesLeft); env.ChannelFilters.Add(plug); break; } default: Console.WriteLine("Unknown envelope type " + reader.GetIDString((uint)id)); break; } } } return env; }
//====================================================================== //outgoing() // //Return the outgoing tangent to the curve at key0. The value returned //for the BEZ2 case is used when extrapolating a linear pre behavior and //when interpolating a non-BEZ2 span. //====================================================================== static float Outgoing(EnvelopeKey keyp0, EnvelopeKey key0, EnvelopeKey key1) { float a, b, d, t, _out; switch (key0.Shape) { case KeyShape.ID_TCB: { a = (1.0f - key0.Tension) * (1.0f + key0.Continuity) * (1.0f + key0.Bias); b = (1.0f - key0.Tension) * (1.0f - key0.Continuity) * (1.0f - key0.Bias); d = key1.Value - key0.Value; if (keyp0 != null) { t = (key1.Time - key0.Time) / (key1.Time - keyp0.Time); _out = t * (a * (key0.Value - keyp0.Value) + b * d); } else _out = b * d; break; } case KeyShape.ID_LINE: { d = key1.Value - key0.Value; if (keyp0 != null) { t = (key1.Time - key0.Time) / (key1.Time - keyp0.Time); _out = t * (key0.Value - keyp0.Value + d); } else _out = d; break; } case KeyShape.ID_BEZI: case KeyShape.ID_HERM: { _out = key0.param[1]; if (keyp0 != null) _out *= (key1.Time - key0.Time) / (key1.Time - keyp0.Time); break; } case KeyShape.ID_BEZ2: { _out = key0.param[3] * (key1.Time - key0.Time); if (Math.Abs(key0.param[2]) > 1e-5f) _out /= key0.param[2]; else _out *= 1e5f; break; } case KeyShape.ID_STEP: default: { _out = 0.0f; break; } } return _out; }