diff options
author | gspencer@google.com <gspencer@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-05-27 23:15:42 +0000 |
---|---|---|
committer | gspencer@google.com <gspencer@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-05-27 23:15:42 +0000 |
commit | 05b47f7a8c5451f858dc220df0e3a97542edace6 (patch) | |
tree | a2273d619f0625c9d44d40842845ccce2eac1045 /o3d/core/cross/curve.cc | |
parent | 5cdc8bdb4c847cefe7f4542bd10c9880c2c557a0 (diff) | |
download | chromium_src-05b47f7a8c5451f858dc220df0e3a97542edace6.zip chromium_src-05b47f7a8c5451f858dc220df0e3a97542edace6.tar.gz chromium_src-05b47f7a8c5451f858dc220df0e3a97542edace6.tar.bz2 |
This is the O3D source tree's initial commit to the Chromium tree. It
is not built or referenced at all by the chrome build yet, and doesn't
yet build in it's new home. We'll change that shortly.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@17035 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'o3d/core/cross/curve.cc')
-rw-r--r-- | o3d/core/cross/curve.cc | 806 |
1 files changed, 806 insertions, 0 deletions
diff --git a/o3d/core/cross/curve.cc b/o3d/core/cross/curve.cc new file mode 100644 index 0000000..9aff37f --- /dev/null +++ b/o3d/core/cross/curve.cc @@ -0,0 +1,806 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the implementation of Curve. +// This code is heavily influenced by code from FCollada. + +#include "core/cross/precompile.h" +#include "core/cross/curve.h" +#include "core/cross/error.h" +#include "import/cross/memory_stream.h" +#include "import/cross/raw_data.h" + +#undef min +#undef max + +namespace o3d { + +O3D_DEFN_CLASS(CurveKey, ObjectBase); +O3D_DEFN_CLASS(StepCurveKey, CurveKey); +O3D_DEFN_CLASS(LinearCurveKey, CurveKey); +O3D_DEFN_CLASS(BezierCurveKey, CurveKey); +O3D_DEFN_CLASS(CurveFunctionContext, FunctionContext); +O3D_DEFN_CLASS(Curve, Function); + +const char *Curve::kSerializationID = "CURV"; + +namespace { + +const float kEpsilon = 0.00001f; + +float Clamp(float value, float min_value, float max_value) { + return value > max_value ? max_value : + (value < min_value ? min_value : value); +} + +// Uses iterative method to accurately pin-point the 't' of the Bezier +// equation that corresponds to the current time. +float FindT(float control_point_0_x, + float control_point_1_x, + float control_point_2_x, + float control_point_3_x, + float input, + float initial_guess) { + float local_tolerance = 0.001f; + float high_t = 1.0f; + float low_t = 0.0f; + + // TODO: Optimize here, start with a more intuitive value than 0.5 + // (comment left over from original code) + float mid_t = 0.5f; + if (initial_guess <= 0.1) { + mid_t = 0.1f; // clamp to 10% or 90%, because if miss, the cost is + // too high. + } else if (initial_guess >= 0.9) { + mid_t = 0.9f; + } else { + mid_t = initial_guess; + } + bool once = true; + while ((high_t-low_t) > local_tolerance) { + if (once) { + once = false; + } else { + mid_t = (high_t - low_t) / 2.0f + low_t; + } + float ti = 1.0f - mid_t; // (1 - t) + float calculated_time = control_point_0_x * ti * ti * ti + + 3 * control_point_1_x * mid_t * ti * ti + + 3 * control_point_2_x * mid_t * mid_t * ti + + control_point_3_x * mid_t * mid_t * mid_t; + if (fabsf(calculated_time - input) <= local_tolerance) { + break; // If we 'fall' very close, we like it and break. + } + if (calculated_time > input) { + high_t = mid_t; + } else { + low_t = mid_t; + } + } + return mid_t; +} + +typedef CurveKey::Ref (*KeyCreatorFunc)(ServiceLocator* service_locator, + Curve* onwer); +struct KeyCreator { + const ObjectBase::Class* key_type; + KeyCreatorFunc create_function; +}; +static KeyCreator g_creators[] = { + { StepCurveKey::GetApparentClass(), StepCurveKey::Create, }, + { LinearCurveKey::GetApparentClass(), LinearCurveKey::Create, }, + { BezierCurveKey::GetApparentClass(), BezierCurveKey::Create, }, +}; + +} // anonymous namespace + +CurveKey::CurveKey(ServiceLocator* service_locator, Curve* owner) + : ObjectBase(service_locator), + owner_(owner), + input_(0.0f), + output_(0.0f) { +} + +void CurveKey::Destroy() { + owner_->RemoveKey(this); +} + +void CurveKey::SetInput(float new_input) { + if (new_input != input_) { + input_ = new_input; + owner_->MarkAsUnsorted(); + } +} + +void CurveKey::SetOutput(float new_output) { + if (new_output != output_) { + output_ = new_output; + owner_->InvalidateCache(); + } +} + +// StepCurveKey ------------------------------------------------------------ + +StepCurveKey::StepCurveKey(ServiceLocator* service_locator, Curve* owner) + : CurveKey(service_locator, owner) { +} + +CurveKey::Ref StepCurveKey::Create(ServiceLocator* service_locator, + Curve* owner) { + return CurveKey::Ref(new StepCurveKey(service_locator, owner)); +} + +float StepCurveKey::GetOutputAtOffset(float offset, unsigned key_index) const { + return output(); +} + +// LinearCurveKey ---------------------------------------------------------- + +LinearCurveKey::LinearCurveKey(ServiceLocator* service_locator, Curve* owner) + : CurveKey(service_locator, owner) { +} + +CurveKey::Ref LinearCurveKey::Create(ServiceLocator* service_locator, + Curve* owner) { + return CurveKey::Ref(new LinearCurveKey(service_locator, owner)); +} + +float LinearCurveKey::GetOutputAtOffset(float offset, + unsigned key_index) const { + const CurveKey* next_key(owner()->GetKey(key_index + 1)); + DCHECK(next_key); + + float input_span = next_key->input() - input(); + float output_span = next_key->output() - output(); + return output() + offset / input_span * output_span; +} + +// BezierCurveKey ---------------------------------------------------------- + +BezierCurveKey::BezierCurveKey(ServiceLocator* service_locator, Curve* owner) + : CurveKey(service_locator, owner), + in_tangent_(0, 0), + out_tangent_(0, 0) { +} + +CurveKey::Ref BezierCurveKey::Create(ServiceLocator* service_locator, + Curve* owner) { + return CurveKey::Ref(new BezierCurveKey(service_locator, owner)); +} + +void BezierCurveKey::SetInTangent(const Float2& value) { + in_tangent_ = value; + owner()->InvalidateCache(); +} + +void BezierCurveKey::SetOutTangent(const Float2& value) { + out_tangent_ = value; + owner()->InvalidateCache(); +} + +float BezierCurveKey::GetOutputAtOffset(float offset, + unsigned key_index) const { + const CurveKey* next_key(owner()->GetKey(key_index + 1)); + DCHECK(next_key); + + float input_span = next_key->input() - input(); + float output_span = next_key->output() - output(); + Float2 in_tangent; + + // We check bezier first because it's the most likely match for another + // bezier key. + if (next_key->GetClass() == BezierCurveKey::GetApparentClass()) { + in_tangent = (down_cast<const BezierCurveKey*>(next_key)->in_tangent()); + } else if (next_key->GetClass() == LinearCurveKey::GetApparentClass() || + next_key->GetClass() == StepCurveKey::GetApparentClass()) { + in_tangent.setX(next_key->input() - input_span / 3.0f); + in_tangent.setY(next_key->output() - output_span / 3.0f); + } else { + DCHECK(false); // Bad Key. + return output(); + } + + // Do a bezier calculation. + float t = offset / input_span; + t = FindT(input(), + out_tangent_.getX(), + in_tangent.getX(), + next_key->input(), + input() + offset, + t); + float b = out_tangent_.getY(); + float c = in_tangent.getY(); + float ti = 1.0f - t; + float br = 3.0f; + float cr = 3.0f; + return output() * ti * ti * ti + br * b * ti * ti * t + + cr * c * ti * t * t + next_key->output() * t * t * t; +} + +// CurveFunctionContext ------------------------------------------------ + +CurveFunctionContext::CurveFunctionContext(ServiceLocator* service_locator) + : FunctionContext(service_locator), + last_key_index_(0) { +} + +// Curve -------------------------------------------------------------- + +const float Curve::kDefaultSampleRate = 1.0f / 30.0f; // 30hz +const float Curve::kMinimumSampleRate = 1.0f / 240.0f; // 240hz + +Curve::Curve(ServiceLocator* service_locator) + : Function(service_locator), + pre_infinity_(CONSTANT), + post_infinity_(CONSTANT), + sorted_(true), + use_cache_(true), + sample_rate_(kDefaultSampleRate), + cache_valid_(false), + check_discontinuity_(false), + discontinuous_(false), + num_step_keys_(0), + last_key_index_(0) { +} + +Curve::~Curve() { +} + +const ObjectBase::Class* Curve::GetFunctionContextClass() const { + return CurveFunctionContext::GetApparentClass(); +} + +FunctionContext* Curve::CreateFunctionContext() const { + return new CurveFunctionContext(service_locator()); +} + +void Curve::AddKey(CurveKey::Ref key) { + keys_.push_back(key); + MarkAsUnsorted(); + if (key->IsA(StepCurveKey::GetApparentClass())) { + ++num_step_keys_; + } +} + +CurveKey* Curve::CreateKeyByClass(const ObjectBase::Class* key_type) { + for (unsigned ii = 0; ii < arraysize(g_creators); ++ii) { + if (g_creators[ii].key_type == key_type) { + CurveKey::Ref key(g_creators[ii].create_function( + service_locator(), + this)); + AddKey(key); + return key.Get(); + } + } + + O3D_ERROR(service_locator()) + << "unrecognized key type '" << (key_type ? key_type->name() : "NULL") + << "'"; + return NULL; +} + +CurveKey* Curve::CreateKeyByClassName(const String& key_type) { + for (unsigned ii = 0; ii < arraysize(g_creators); ++ii) { + if (!key_type.compare(g_creators[ii].key_type->name()) || + !key_type.compare(g_creators[ii].key_type->unqualified_name())) { + CurveKey::Ref key(g_creators[ii].create_function( + service_locator(), + this)); + AddKey(key); + return key.Get(); + } + } + + O3D_ERROR(service_locator()) + << "unrecognized key type '" << key_type << "'"; + return NULL; +} + +void Curve::RemoveKey(CurveKey* key) { + // keep a reference to the key so it doesn't get deleted early. + CurveKey::Ref temp(key); + CurveKeyRefArray::iterator end = std::remove(keys_.begin(), + keys_.end(), + CurveKey::Ref(key)); + + // key should never be in the key array more than once. + DLOG_ASSERT(std::distance(end, keys_.end()) <= 1); + + // The key was never found. + DCHECK(end != keys_.end()); + + if (key->IsA(StepCurveKey::GetApparentClass())) { + --num_step_keys_; + } + + // Actually remove the key from the key array + keys_.erase(end, keys_.end()); + + InvalidateCache(); + + // The key will get destroyed here since the last reference to + // it, temp, will be released. +} + +void Curve::SetSampleRate(float rate) { + if (rate < kMinimumSampleRate) { + // TODO: should we just silently set it to the minimum? + O3D_ERROR(service_locator()) + << "attempt to set sample rate to " << rate + << " which is lower than the minimum of " + << kMinimumSampleRate; + } else if (rate != sample_rate_) { + sample_rate_ = rate; + InvalidateCache(); + } +} + +void Curve::MarkAsUnsorted() const { + sorted_ = false; +} + +void Curve::InvalidateCache() const { + cache_valid_ = false; + check_discontinuity_ = true; +} + +bool Curve::IsDiscontinuous() const { + UpdateCurveInfo(); + return discontinuous_; +} + +inline static bool CompareByInput(const CurveKey::Ref& lhs, + const CurveKey::Ref& rhs) { + return lhs->input() < rhs->input(); +} + +void Curve::ResortKeys() const { + std::sort(keys_.begin(), keys_.end(), CompareByInput); + sorted_ = true; + InvalidateCache(); +} + +void Curve::CheckDiscontinuity() const { + // Mark the curve as discontinuous if any 2 keys share the same input and + // if their outputs are different. + check_discontinuity_ = false; + discontinuous_ = num_step_keys_ > 0 && num_step_keys_ != keys_.size(); + if (!discontinuous_ && keys_.size() > 1) { + for (unsigned ii = 0; ii < keys_.size() - 1; ++ii) { + if (keys_[ii]->input() == keys_[ii + 1]->input() && + keys_[ii]->output() != keys_[ii + 1]->output()) { + discontinuous_ = true; + break; + } + } + } +} + +void Curve::CreateCache() const { + float start_input = keys_.front()->input(); + float end_input = keys_.back()->input(); + float input_span = end_input - start_input; + + unsigned samples = + static_cast<unsigned>(ceilf(input_span / sample_rate_) + 1); + cache_samples_.clear(); + cache_samples_.resize(samples, 0.0f); + + FunctionContext::Ref context = FunctionContext::Ref(CreateFunctionContext()); + + for (unsigned ii = 0; ii < samples; ++ii) { + cache_samples_[ii] = GetOutputInSpan( + start_input + sample_rate_ * static_cast<float>(ii), + down_cast<CurveFunctionContext*>(context.Get())); + } + + cache_valid_ = true; +} + +float Curve::GetOutputInSpan(float input, CurveFunctionContext* context) const { + DCHECK(input >= keys_.front()->input()); + + if (input >= keys_.back()->input()) { + return keys_.back()->output(); + } + + // use the keys directly. + unsigned start = 0; + unsigned end = keys_.size(); + unsigned key_index; + bool found = false; + + static const unsigned kKeysToSearch = 3U; + + // See if the context already has a index to the correct key. + if (context) { + key_index = context->last_key_index(); + // is that index in range. + if (key_index < end - 1) { + // Are we between these keys. + if (keys_[key_index]->input() <= input && + keys_[key_index + 1]->input() > input) { + // Yes! + found = true; + } else { + // No, so check which way we need to go. + if (input > keys_[key_index]->input()) { + // search forward a few keys. If it's not within a few keys give up. + unsigned check_end = std::max(key_index + kKeysToSearch, end); + for (++key_index; key_index < check_end; ++key_index) { + if (keys_[key_index]->input() <= input && + keys_[key_index + 1]->input() > input) { + // Yes! + found = true; + break; + } + } + } else if (key_index > 0) { + // search backward a few keys. If it's not within a few keys give up. + unsigned check_end = std::min(key_index - kKeysToSearch, 0U); + for (--key_index; key_index >= check_end; --key_index) { + if (keys_[key_index]->input() <= input && + keys_[key_index + 1]->input() > input) { + // Yes! + found = true; + break; + } + } + } + } + } + } + + if (!found) { + // TODO: If we assume the most common case is sampled keys and + // constant intervals we can make a quick guess where that key is. + + // Find the current the keys that cover our input. + while (start <= end) { + unsigned mid = (start + end) / 2; + if (input > keys_[mid]->input()) { + start = mid + 1; + } else { + if (mid == 0) { + break; + } + end = mid - 1; + } + } + + end = keys_.size(); + while (start < end) { + if (keys_[start]->input() > input) { + break; + } + ++start; + } + + DCHECK(start > 0); + DCHECK(start < end); + + key_index = start - 1; + } + + const CurveKey* key = keys_[key_index]; + if (context) { + context->set_last_key_index(key_index); + } + return key->GetOutputAtOffset(input - key->input(), key_index); +} + +float Curve::Evaluate(float input, FunctionContext* context) const { + if (context && !context->IsA(CurveFunctionContext::GetApparentClass())) { + O3D_ERROR(service_locator()) + << "function context '" << context->GetClassName() + << "' is wrong type for Curve"; + context = NULL; + } + + if (keys_.empty()) { + return 0.0f; + } + + if (keys_.size() == 1) { + return keys_.front()->output(); + } + + UpdateCurveInfo(); + + float start_input = keys_.front()->input(); + float end_input = keys_.back()->input(); + float input_span = end_input - start_input; + float start_output = keys_.front()->output(); + float end_output = keys_.back()->output(); + float output_delta = end_output - start_output; + + float output_offset = 0.0f; + // check for pre-infinity + if (input < start_input) { + if (input_span <= 0.0f) { + return start_output; + } + float pre_infinity_offset = start_input - input; + switch (pre_infinity_) { + case CONSTANT: + return start_output; + case LINEAR: { + const CurveKey* second_key = keys_[1]; + float input_delta = second_key->input() - start_input; + if (input_delta > kEpsilon) { + return start_output - pre_infinity_offset * + (second_key->output() - start_output) / input_delta; + } else { + return start_output; + } + } + case CYCLE: { + float cycle_count = ceilf(pre_infinity_offset / input_span); + input += cycle_count * input_span; + input = start_input + fmodf(input - start_input, input_span); + break; + } + case CYCLE_RELATIVE: { + float cycle_count = ceilf(pre_infinity_offset / input_span); + input += cycle_count * input_span; + input = start_input + fmodf(input - start_input, input_span); + output_offset -= cycle_count * output_delta; + break; + } + case OSCILLATE: { + float cycle_count = ceilf(pre_infinity_offset / (2.0f * input_span)); + input += cycle_count * 2.0f * input_span; + input = end_input - fabsf(input - end_input); + break; + } + default: + O3D_ERROR(service_locator()) << "invalid value for pre-infinity"; + return start_output; + } + } else if (input >= end_input) { + // check for post-infinity + if (input_span <= 0.0f) { + return end_output; + } + float post_infinity_offset = input - end_input; + switch (post_infinity_) { + case CONSTANT: + return end_output; + case LINEAR: { + const CurveKey* next_to_last_key = keys_[keys_.size() - 2]; + float input_delta = end_input - next_to_last_key->input(); + if (input_delta > kEpsilon) { + return end_output + post_infinity_offset * + (end_output - next_to_last_key->output()) / + input_delta; + } else { + return end_output; + } + } + case CYCLE: { + float cycle_count = ceilf(post_infinity_offset / input_span); + input -= cycle_count * input_span; + input = start_input + fmodf(input - start_input, input_span); + break; + } + case CYCLE_RELATIVE: { + float cycle_count = floorf((input - start_input) / input_span); + input -= cycle_count * input_span; + input = start_input + fmodf(input - start_input, input_span); + output_offset += cycle_count * output_delta; + break; + } + case OSCILLATE: { + float cycle_count = ceilf(post_infinity_offset / (2.0f * + input_span)); + input -= cycle_count * 2.0f * input_span; + input = start_input + fabsf(input - start_input); + break; + } + default: + O3D_ERROR(service_locator()) << "invalid value for post-infinity"; + return end_output; + } + } + + // At this point input should be between start_input and end_input + // inclusive. + + // If we are at end_input then just return end_output since we can't + // interpolate end_input to anything past it. + if (input >= end_input) { + return end_output + output_offset; + } + + if (!discontinuous_ && use_cache_) { + // use the cache. The cache is implemented as a sampling of outputs at + // specific intervals (sample_rate_). Linear interpolation is used between + // samples. The is significantly faster than using the real keys but it + // takes a lot more memory and it can't handle discontinuous animation like + // camera cuts. + if (!cache_valid_) { + CreateCache(); + } + float span_input = input - start_input; + unsigned sample = static_cast<unsigned>(span_input / sample_rate_); + + DCHECK(sample < cache_samples_.size() - 1); + + float current_sample = cache_samples_[sample]; + if (num_step_keys_ == keys_.size()) { + // It's all step keys so don't interpolate. + return current_sample + output_offset; + } else { + float offset = fmodf(span_input, sample_rate_); + float next_sample = cache_samples_[sample + 1]; + return current_sample + (next_sample - current_sample) * + offset / sample_rate_ + output_offset; + } + } else { + return GetOutputInSpan( + input, down_cast<CurveFunctionContext*>(context)) + output_offset; + } +} + +ObjectBase::Ref Curve::Create(ServiceLocator* service_locator) { + return ObjectBase::Ref(new Curve(service_locator)); +} + + +static Float2 ReadFloat2(MemoryReadStream *stream) { + float v1 = stream->ReadLittleEndianFloat32(); + float v2 = stream->ReadLittleEndianFloat32(); + Float2 value; + value.setX(v1); + value.setY(v2); + return value; +} + +// Some constants for error checking +const size_t kFloat2Size = 2 * sizeof(float); +const size_t kStepDataSize = 2 * sizeof(float); +const size_t kLinearDataSize = 2 * sizeof(float); + +// not using sizeof(Float2) just in case this class adds ivars +// we're being more explicit here +const size_t kBezierDataSize = 2 * sizeof(float) + 2 * kFloat2Size; + +bool Curve::LoadFromBinaryData(MemoryReadStream *stream) { + // Make sure we have enough data for serialization ID and version + if (stream->GetRemainingByteCount() < 4 + sizeof(int32)) { + O3D_ERROR(service_locator()) << "invalid empty curve data"; + return false; + } + + // To insure data integrity we expect four characters kSerializationID + uint8 id[4]; + stream->Read(id, 4); + + if (memcmp(id, kSerializationID, 4)) { + O3D_ERROR(service_locator()) + << "data object does not contain curve data"; + return false; + } + + int32 version = stream->ReadLittleEndianInt32(); + if (version != 1) { + O3D_ERROR(service_locator()) << "unknown version for curve data"; + return false; + } + + while (!stream->EndOfStream()) { + // switch on types of keys + CurveKey::KeyType key_type = + static_cast<CurveKey::KeyType>(stream->ReadByte()); + size_t available_bytes = stream->GetRemainingByteCount(); + + switch (key_type) { + case CurveKey::TYPE_STEP: { + if (available_bytes < kStepDataSize) { + O3D_ERROR(service_locator()) << "unexpected end of curve data"; + return false; + } + float input = stream->ReadLittleEndianFloat32(); + float output = stream->ReadLittleEndianFloat32(); + StepCurveKey* key = Create<StepCurveKey>(); + key->SetInput(input); + key->SetOutput(output); + break; + } + case CurveKey::TYPE_LINEAR: { + if (available_bytes < kLinearDataSize) { + O3D_ERROR(service_locator()) << "unexpected end of curve data"; + return false; + } + float input = stream->ReadLittleEndianFloat32(); + float output = stream->ReadLittleEndianFloat32(); + LinearCurveKey* key = Create<LinearCurveKey>(); + key->SetInput(input); + key->SetOutput(output); + break; + } + case CurveKey::TYPE_BEZIER: { + if (available_bytes < kBezierDataSize) { + O3D_ERROR(service_locator()) << "unexpected end of curve data"; + return false; + } + float input = stream->ReadLittleEndianFloat32(); + float output = stream->ReadLittleEndianFloat32(); + Float2 in_tangent = ReadFloat2(stream); + Float2 out_tangent = ReadFloat2(stream); + BezierCurveKey* key = Create<BezierCurveKey>(); + key->SetInput(input); + key->SetOutput(output); + key->SetInTangent(in_tangent); + key->SetOutTangent(out_tangent); + break; + } + default: { + O3D_ERROR(service_locator()) << "invalid curve data"; + return false; // unknown key type + } + } + } + return true; +} + +bool Curve::Set(o3d::RawData *raw_data) { + if (!raw_data) { + O3D_ERROR(service_locator()) << "data object is null"; + return false; + } + return Set(raw_data, 0, raw_data->GetLength()); +} + +bool Curve::Set(o3d::RawData *raw_data, + size_t offset, + size_t length) { + if (!raw_data) { + O3D_ERROR(service_locator()) << "data object is null"; + return false; + } + + if (!raw_data->IsOffsetLengthValid(offset, length)) { + O3D_ERROR(service_locator()) << "illegal curve data offset or size"; + return false; + } + + const uint8 *data = raw_data->GetDataAs<uint8>(offset); + if (!data) { + return false; + } + + MemoryReadStream stream(data, length); + return LoadFromBinaryData(&stream); +} + +} // namespace o3d |