summaryrefslogtreecommitdiffstats
path: root/o3d/import/cross/collada.cc
diff options
context:
space:
mode:
authorbradnelson@google.com <bradnelson@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2011-03-28 20:23:26 +0000
committerbradnelson@google.com <bradnelson@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2011-03-28 20:23:26 +0000
commitd15f03f7d3aaa253d32c8bfc4f1543f5f9d6eeae (patch)
treee3ae9df25c03721d2889ca4aad346dc7c2d99363 /o3d/import/cross/collada.cc
parente6111af1609505398801eed7619a2f7191fe3a2b (diff)
downloadchromium_src-d15f03f7d3aaa253d32c8bfc4f1543f5f9d6eeae.zip
chromium_src-d15f03f7d3aaa253d32c8bfc4f1543f5f9d6eeae.tar.gz
chromium_src-d15f03f7d3aaa253d32c8bfc4f1543f5f9d6eeae.tar.bz2
Moving o3d up a level, to get it out of chrome checkouts.
BUG=None TEST=None Too large for codereview. Manual review by thaloun and tschelcher. git-svn-id: svn://svn.chromium.org/chrome/trunk/src@79609 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'o3d/import/cross/collada.cc')
-rw-r--r--o3d/import/cross/collada.cc3079
1 files changed, 0 insertions, 3079 deletions
diff --git a/o3d/import/cross/collada.cc b/o3d/import/cross/collada.cc
deleted file mode 100644
index 6047da0..0000000
--- a/o3d/import/cross/collada.cc
+++ /dev/null
@@ -1,3079 +0,0 @@
-/*
- * 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 functions for importing COLLADA files into O3D.
-#include "import/cross/precompile.h"
-
-#include "base/file_path.h"
-#include "base/file_util.h"
-#include "base/process_util.h"
-#include "base/utf_string_conversions.h"
-#include "core/cross/class_manager.h"
-#include "core/cross/curve.h"
-#include "core/cross/error.h"
-#include "core/cross/function.h"
-#include "core/cross/ierror_status.h"
-#include "core/cross/math_utilities.h"
-#include "core/cross/matrix4_axis_rotation.h"
-#include "core/cross/matrix4_composition.h"
-#include "core/cross/matrix4_scale.h"
-#include "core/cross/matrix4_translation.h"
-#include "core/cross/pack.h"
-#include "core/cross/param_operation.h"
-#include "core/cross/primitive.h"
-#include "core/cross/sampler.h"
-#include "core/cross/skin.h"
-#include "core/cross/stream.h"
-#include "import/cross/collada.h"
-#include "import/cross/collada_conditioner.h"
-#include "import/cross/collada_zip_archive.h"
-#include "import/cross/destination_buffer.h"
-#include "import/cross/file_output_stream_processor.h"
-#include "utils/cross/file_path_utils.h"
-#include "third_party/libtxc_dxtn/files/txc_dxtn.h"
-
-#define COLLADA_NAMESPACE "collada"
-#define COLLADA_NAMESPACE_SEPARATOR "."
-
-// Macro to provide a uniform prefix for all string constants created by
-// COLLADA.
-#define COLLADA_STRING_CONSTANT(value) \
- (COLLADA_NAMESPACE COLLADA_NAMESPACE_SEPARATOR value)
-
-
-namespace o3d {
-
-const char* Collada::kLightingTypeParamName =
- COLLADA_STRING_CONSTANT("lightingType");
-
-const char* Collada::kLightingTypeConstant = "constant";
-const char* Collada::kLightingTypePhong = "phong";
-const char* Collada::kLightingTypeBlinn = "blinn";
-const char* Collada::kLightingTypeLambert = "lambert";
-const char* Collada::kLightingTypeUnknown = "unknown";
-
-const char* Collada::kMaterialParamNameEmissive = "emissive";
-const char* Collada::kMaterialParamNameAmbient = "ambient";
-const char* Collada::kMaterialParamNameDiffuse = "diffuse";
-const char* Collada::kMaterialParamNameSpecular = "specular";
-const char* Collada::kMaterialParamNameShininess = "shininess";
-const char* Collada::kMaterialParamNameSpecularFactor = "specularFactor";
-const char* Collada::kMaterialParamNameEmissiveSampler = "emissiveSampler";
-const char* Collada::kMaterialParamNameAmbientSampler = "ambientSampler";
-const char* Collada::kMaterialParamNameDiffuseSampler = "diffuseSampler";
-const char* Collada::kMaterialParamNameSpecularSampler = "specularSampler";
-const char* Collada::kMaterialParamNameBumpSampler = "bumpSampler";
-
-class TranslationMap : public FCDGeometryIndexTranslationMap {
-};
-
-namespace {
-
-Vector3 FMVector3ToVector3(const FMVector3& fmvector3) {
- return Vector3(fmvector3.x, fmvector3.y, fmvector3.z);
-}
-
-Vector4 FMVector4ToVector4(const FMVector4& fmvector4) {
- return Vector4(fmvector4.x, fmvector4.y, fmvector4.z, fmvector4.w);
-}
-
-Float2 FMVector2ToFloat2(const FMVector2& fmvector2) {
- return Float2(fmvector2.x, fmvector2.y);
-}
-
-Matrix4 FMMatrix44ToMatrix4(const FMMatrix44& fmmatrix44) {
- return Matrix4(Vector4(fmmatrix44[0][0],
- fmmatrix44[0][1],
- fmmatrix44[0][2],
- fmmatrix44[0][3]),
- Vector4(fmmatrix44[1][0],
- fmmatrix44[1][1],
- fmmatrix44[1][2],
- fmmatrix44[1][3]),
- Vector4(fmmatrix44[2][0],
- fmmatrix44[2][1],
- fmmatrix44[2][2],
- fmmatrix44[2][3]),
- Vector4(fmmatrix44[3][0],
- fmmatrix44[3][1],
- fmmatrix44[3][2],
- fmmatrix44[3][3]));
-}
-} // anonymous namespace
-
-void ColladaDataMap::Clear() {
- original_data_.clear();
-}
-
-bool ColladaDataMap::AddData(const FilePath& file_path,
- const std::string& data,
- ServiceLocator* service_locator) {
- std::pair<OriginalDataMap::iterator, bool> result =
- original_data_.insert(std::pair<FilePath, std::string>(file_path, data));
- if (!result.second) {
- O3D_ERROR(service_locator)
- << "Attempt to map 2 resources to the same file path:"
- << FilePathToUTF8(file_path).c_str();
- }
- return result.second;
-}
-
-std::vector<FilePath> ColladaDataMap::GetOriginalDataFilenames() const {
- std::vector<FilePath> result;
- for (OriginalDataMap::const_iterator iter = original_data_.begin();
- iter != original_data_.end();
- ++iter) {
- result.push_back(iter->first);
- }
- return result;
-}
-
-const std::string& ColladaDataMap::GetOriginalData(
- const FilePath& filename) const {
- static const std::string empty;
- OriginalDataMap::const_iterator entry = original_data_.find(filename);
- if (entry != original_data_.end()) {
- return entry->second;
- } else {
- return empty;
- }
-}
-
-// Import the given COLLADA file or ZIP file into the given scene.
-// This is the external interface to o3d.
-bool Collada::Import(Pack* pack,
- const FilePath& filename,
- Transform* parent,
- ParamFloat* animation_input,
- const Options& options) {
- Collada collada(pack, options);
- return collada.ImportFile(filename, parent, animation_input);
-}
-
-bool Collada::Import(Pack* pack,
- const String& filename,
- Transform* parent,
- ParamFloat* animation_input,
- const Options& options) {
- return Collada::Import(pack,
- UTF8ToFilePath(filename),
- parent,
- animation_input,
- options);
-}
-
-void Collada::Init(ServiceLocator* service_locator) {
- ClassManager* class_manager=
- service_locator->GetService<o3d::ClassManager>();
- class_manager->AddTypedClass<DestinationBuffer>();
-}
-
-// Parameters:
-// pack: The pack into which the scene objects will be placed.
-// Returns true on success.
-Collada::Collada(Pack* pack, const Options& options)
- : service_locator_(pack->service_locator()),
- pack_(pack),
- options_(options),
- dummy_effect_(NULL),
- dummy_material_(NULL),
- instance_root_(NULL),
- collada_zip_archive_(NULL),
- cull_enabled_(false),
- cull_front_(false),
- front_cw_(false),
- unique_filename_counter_(0) {
-}
-
-Collada::~Collada() {
- delete collada_zip_archive_;
-}
-
-void Collada::ClearData() {
- textures_.clear();
- original_data_map_.Clear();
- effects_.clear();
- shapes_.clear();
- skinned_shapes_.clear();
- materials_.clear();
- delete collada_zip_archive_;
- collada_zip_archive_ = NULL;
- cull_enabled_ = false;
- cull_front_ = false;
- front_cw_ = false;
- delete instance_root_;
- instance_root_ = NULL;
- base_path_ = FilePath(FilePath::kCurrentDirectory);
- unique_filename_counter_ = 0;
-}
-
-// Import the given COLLADA file or ZIP file under the given parent node.
-// Parameters:
-// filename: The COLLADA or ZIPped COLLADA file to import.
-// parent: The parent node under which the imported nodes will be
-// placed. If NULL, nodes will be placed under the
-// client's root.
-// animation_input: The float parameter used as the input to the animation
-// (if present). This is usually time. This can be null
-// if there is no animation.
-// Returns true on success.
-bool Collada::ImportFile(const FilePath& filename, Transform* parent,
- ParamFloat* animation_input) {
- // Each time we start a new import, we need to clear out data from
- // the last import (if any).
- ClearData();
-
- // Convert the base_path given in the options to an absolute path.
- base_path_ = options_.base_path;
- file_util::AbsolutePath(&base_path_);
-
- bool status = false;
- if (ZipArchive::IsZipFile(FilePathToUTF8(filename))) {
- status = ImportZIP(filename, parent, animation_input);
- } else {
- status = ImportDAE(filename, parent, animation_input);
- }
-
- if (!status) {
- // TODO(o3d): this could probably be the original URI instead of some
- // filename in the temp folder.
- O3D_ERROR(service_locator_) << "Unable to import: "
- << FilePathToUTF8(filename).c_str();
- }
-
- return status;
-}
-
-// Imports the given ZIP file into the given client.
-// Parameters: see Import() above.
-// Returns true on success.
-bool Collada::ImportZIP(const FilePath& filename, Transform* parent,
- ParamFloat* animation_input) {
- // This uses minizip, which avoids decompressing the zip archive to a
- // temp directory...
- //
- bool status = false;
- int result = 0;
-
- String filename_str = FilePathToUTF8(filename);
- collada_zip_archive_ = new ColladaZipArchive(filename_str, &result);
-
- if (result == UNZ_OK) {
- FCollada::Initialize();
- FCDocument* doc = FCollada::NewTopDocument();
-
- if (doc) {
- std::string model_path = collada_zip_archive_->GetColladaPath().c_str();
-
- size_t doc_buffer_size = 0;
- char *doc_buffer = collada_zip_archive_->GetFileData(model_path,
- &doc_buffer_size);
-
- if (doc_buffer && doc_buffer_size > 0) {
- DLOG(INFO) << "Loading Collada model \""
- << model_path << "\" from zip file \""
- << filename_str << "\"";
-
- std::wstring model_path_w = UTF8ToWide(model_path);
-
- bool fc_status = FCollada::LoadDocumentFromMemory(model_path_w.c_str(),
- doc,
- doc_buffer,
- doc_buffer_size);
-
-
- if (fc_status) {
- if (options_.condition_document) {
- ColladaConditioner conditioner(service_locator_);
- if (conditioner.ConditionDocument(doc, collada_zip_archive_)) {
- status = ImportDAEDocument(doc,
- fc_status,
- parent,
- animation_input);
- }
- } else {
- status = ImportDAEDocument(doc, fc_status, parent, animation_input);
- }
- }
- }
- doc->Release();
- }
- FCollada::Release();
- }
-
- if (!status) {
- delete collada_zip_archive_;
- collada_zip_archive_ = NULL;
- }
-
- return status;
-}
-
-// Imports the given COLLADA file (.DAE) into the given pack.
-// Parameters:
-// see Import() above.
-// Returns:
-// true on success.
-bool Collada::ImportDAE(const FilePath& filename,
- Transform* parent,
- ParamFloat* animation_input) {
- if (!parent) {
- return false;
- }
- bool status = false;
- FCollada::Initialize();
- FCDocument* doc = FCollada::NewTopDocument();
- if (doc) {
- std::wstring filename_w = FilePathToWide(filename);
- bool fc_status = FCollada::LoadDocumentFromFile(doc, filename_w.c_str());
- if (options_.condition_document) {
- ColladaConditioner conditioner(service_locator_);
- if (conditioner.ConditionDocument(doc, NULL)) {
- status = ImportDAEDocument(doc, fc_status, parent, animation_input);
- }
- } else {
- status = ImportDAEDocument(doc, fc_status, parent, animation_input);
- }
- doc->Release();
- }
- FCollada::Release();
- return status;
-}
-
-// Imports the given FCDocument document (already loaded) into the given pack.
-// Returns:
-// true on success.
-bool Collada::ImportDAEDocument(FCDocument* doc,
- bool fc_status,
- Transform* parent,
- ParamFloat* animation_input) {
- if (!parent) {
- return false;
- }
- bool status = false;
- if (doc) {
- if (fc_status) {
- Vector3 up_axis = options_.up_axis;
- FMVector3 up(up_axis.getX(), up_axis.getY(), up_axis.getZ());
- // Transform the document to the given up vector
-
- FCDocumentTools::StandardizeUpAxisAndLength(doc, up);
-
- // Import all the textures in the file. Even if they are not used by
- // materials or models the user put them in the file and might need them
- // at runtime.
- //
- // TODO(o3d): Add option to skip this step if user just wants what's
- // actually used by models. The rest of the code already deals with this.
- FCDImageLibrary* image_library = doc->GetImageLibrary();
- for (uint32 i = 0; i < image_library->GetEntityCount(); i++) {
- FCDEntity* entity = image_library->GetEntity(i);
- LOG_ASSERT(entity);
- LOG_ASSERT(entity->GetType() == FCDEntity::IMAGE);
- FCDImage* image = down_cast<FCDImage*>(entity);
- BuildTextureFromImage(image);
- }
-
- // Import all the materials in the file. Even if they are not used by
- // models the user put them in the file and might need them at runtime.
- //
- // TODO(o3d): Add option to skip this step if user just wants what's
- // actually used by models. The rest of the code already deals with this.
- FCDMaterialLibrary* material_library = doc->GetMaterialLibrary();
- for (uint32 i = 0; i < material_library->GetEntityCount(); i++) {
- FCDEntity* entity = material_library->GetEntity(i);
- LOG_ASSERT(entity);
- LOG_ASSERT(entity->GetType() == FCDEntity::MATERIAL);
- FCDMaterial* collada_material = down_cast<FCDMaterial*>(entity);
- BuildMaterial(doc, collada_material);
- }
-
- // Import the scene objects, starting at the root.
- FCDSceneNode* scene = doc->GetVisualSceneInstance();
- if (scene) {
- instance_root_ = CreateInstanceTree(scene);
- if (ImportTree(instance_root_, parent, animation_input)) {
- if (ImportTreeInstances(doc, instance_root_)) {
- status = true;
- }
- }
- delete instance_root_;
- instance_root_ = NULL;
- }
- }
- }
- return status;
-}
-
-namespace {
-
-Curve::Infinity ConvertInfinity(FUDaeInfinity::Infinity infinity) {
- switch (infinity) {
- case FUDaeInfinity::LINEAR :
- return Curve::LINEAR;
- case FUDaeInfinity::CYCLE :
- return Curve::CYCLE;
- case FUDaeInfinity::CYCLE_RELATIVE :
- return Curve::CYCLE_RELATIVE;
- case FUDaeInfinity::OSCILLATE :
- return Curve::OSCILLATE;
- default:
- return Curve::CONSTANT;
- }
-}
-
-StepCurveKey* BuildStepKey(Curve* curve, FCDAnimationKey* fcd_key,
- float output_scale) {
- StepCurveKey* key = curve->Create<StepCurveKey>();
- key->SetInput(fcd_key->input);
- key->SetOutput(fcd_key->output * output_scale);
- return key;
-}
-
-LinearCurveKey* BuildLinearKey(Curve* curve, FCDAnimationKey* fcd_key,
- float output_scale) {
- LinearCurveKey* key = curve->Create<LinearCurveKey>();
- key->SetInput(fcd_key->input);
- key->SetOutput(fcd_key->output * output_scale);
- return key;
-}
-
-BezierCurveKey* BuildBezierKey(Curve* curve, FCDAnimationKeyBezier* fcd_key,
- float output_scale) {
- BezierCurveKey* key = curve->Create<BezierCurveKey>();
- key->SetInput(fcd_key->input);
- key->SetOutput(fcd_key->output * output_scale);
- Float2 in_tangent = FMVector2ToFloat2(fcd_key->inTangent);
- in_tangent[1] *= output_scale;
- key->SetInTangent(in_tangent);
- Float2 out_tangent = FMVector2ToFloat2(fcd_key->outTangent);
- out_tangent[1] *= output_scale;
- key->SetOutTangent(out_tangent);
- return key;
-}
-
-void BindParams(ParamObject* input_object, const char* input_param_name,
- ParamObject* output_object, const char* output_param_name) {
- Param* input_param = input_object->GetUntypedParam(input_param_name);
- DCHECK(input_param);
-
- Param* output_param = output_object->GetUntypedParam(output_param_name);
- DCHECK(output_param);
-
- bool ok = input_param->Bind(output_param);
- DCHECK_EQ(ok, true);
-}
-
-void BindParams(ParamObject* input_object, const char* input_param_name,
- Param* output_param) {
- Param* input_param = input_object->GetUntypedParam(input_param_name);
- DCHECK(input_param);
-
- bool ok = input_param->Bind(output_param);
- DCHECK_EQ(ok, true);
-}
-
-void BindParams(Param* input_param,
- ParamObject* output_object, const char* output_param_name) {
- Param* output_param = output_object->GetUntypedParam(output_param_name);
- DCHECK(output_param);
-
- bool ok = input_param->Bind(output_param);
- DCHECK_EQ(ok, true);
-}
-
-// Runs effect_string through the filter, replacing it in place.
-bool ConvertCgToGlsl(const FilePath& converter, String* effect_string) {
- FilePath temporary_file_name;
- FILE* temporary_file = file_util::CreateAndOpenTemporaryFile(
- &temporary_file_name);
- if (!temporary_file)
- return false;
-
- fwrite(effect_string->c_str(), 1, effect_string->length(), temporary_file);
- effect_string->clear();
- file_util::CloseFile(temporary_file);
-
-#if defined(OS_WIN)
- // Assumes python.exe is in PATH. Doesn't seem there's an easy way
- // to test whether it is without launching a process.
- FilePath python(FILE_PATH_LITERAL("python.exe"));
- CommandLine cmd_line(python);
- cmd_line.AppendLooseValue(FilePathToWide(converter));
-#else
- CommandLine cmd_line(converter);
-#endif
-
- cmd_line.AppendLooseValue(L"-i");
- std::wstring temporary_file_string =
- o3d::FilePathToWide(temporary_file_name);
-#if defined(OS_WIN)
- // Quote to be safe. Note that this breaks on POSIX platforms.
- std::wstring quote(L"\"");
- temporary_file_string =
- quote + temporary_file_string + quote;
-#endif
- cmd_line.AppendLooseValue(temporary_file_string);
- bool rc = ::base::GetAppOutput(cmd_line, effect_string);
-
- file_util::Delete(temporary_file_name, false);
- return rc;
-}
-} // namespace anonymous
-
-bool Collada::BuildFloatAnimation(ParamFloat* result,
- FCDAnimated* animated,
- const char* qualifier,
- ParamFloat* animation_input,
- float output_scale,
- float default_value) {
- if (animated != NULL) {
- FCDAnimationCurve* fcd_curve = animated->FindCurve(qualifier);
- if (fcd_curve != NULL) {
- FunctionEval* function_eval = pack_->Create<FunctionEval>();
- BindParams(function_eval, FunctionEval::kInputParamName, animation_input);
-
- Curve* curve = pack_->Create<Curve>();
- function_eval->set_function_object(curve);
-
- curve->set_pre_infinity(ConvertInfinity(fcd_curve->GetPreInfinity()));
- curve->set_post_infinity(ConvertInfinity(fcd_curve->GetPostInfinity()));
-
- for (unsigned int i = 0; i != fcd_curve->GetKeyCount(); ++i) {
- FCDAnimationKey* fcd_key = fcd_curve->GetKey(i);
- switch (fcd_key->interpolation) {
- case FUDaeInterpolation::STEP:
- BuildStepKey(curve, fcd_key, output_scale);
- break;
- case FUDaeInterpolation::BEZIER:
- BuildBezierKey(curve, static_cast<FCDAnimationKeyBezier*>(fcd_key),
- output_scale);
- break;
- default:
- BuildLinearKey(curve, fcd_key, output_scale);
- break;
- }
- }
-
- BindParams(result, function_eval, FunctionEval::kOutputParamName);
- return true;
- }
- }
-
- result->set_value(default_value * output_scale);
- return false;
-}
-
-bool Collada::BuildFloat3Animation(ParamFloat3* result, FCDAnimated* animated,
- ParamFloat* animation_input,
- const Float3& default_value) {
- ParamOp3FloatsToFloat3* to_float3 =
- pack_->Create<ParamOp3FloatsToFloat3>();
-
- static const char* const kQualifiers[3] = { ".X", ".Y", ".Z" };
- static const char* const kInputs[3] = {
- ParamOp3FloatsToFloat3::kInput0ParamName,
- ParamOp3FloatsToFloat3::kInput1ParamName,
- ParamOp3FloatsToFloat3::kInput2ParamName,
- };
- bool any_animated = false;
- for (int i = 0; i != 3; ++i) {
- ParamFloat* to_float3_input = to_float3->GetParam<ParamFloat>(kInputs[i]);
- any_animated |= BuildFloatAnimation(to_float3_input,
- animated, kQualifiers[i],
- animation_input, 1.0f,
- default_value[i]);
- }
-
- if (any_animated) {
- BindParams(result, to_float3, ParamOp3FloatsToFloat3::kOutputParamName);
- return true;
- } else {
- pack_->RemoveObject(to_float3);
- result->set_value(default_value);
- return false;
- }
-}
-
-ParamMatrix4* Collada::BuildComposition(FCDTMatrix* transform,
- ParamMatrix4* input_matrix,
- ParamFloat* animation_input) {
- Matrix4Composition* composition = pack_->Create<Matrix4Composition>();
- Matrix4 matrix = FMMatrix44ToMatrix4(transform->ToMatrix());
-
- ParamOp16FloatsToMatrix4* to_matrix4 =
- pack_->Create<ParamOp16FloatsToMatrix4>();
- bool any_animated = false;
- for (int i = 0; i != 16; ++i) {
- int row = i / 4;
- int column = i % 4;
-
- std::stringstream input_name;
- input_name << "input" << i;
- ParamFloat* to_matrix4_input = to_matrix4->GetParam<ParamFloat>(
- input_name.str().c_str());
-
- std::stringstream qualifier;
- qualifier << "(" << row << ")" << "(" << column << ")";
- any_animated |= BuildFloatAnimation(to_matrix4_input,
- transform->GetAnimated(),
- qualifier.str().c_str(),
- animation_input, 1.0f,
- matrix[row][column]);
- }
-
- if (any_animated) {
- BindParams(composition, Matrix4Composition::kLocalMatrixParamName,
- to_matrix4, ParamOp16FloatsToMatrix4::kOutputParamName);
- } else {
- pack_->RemoveObject(to_matrix4);
- composition->set_local_matrix(matrix);
- }
-
- BindParams(composition, Matrix4Composition::kInputMatrixParamName,
- input_matrix);
- return composition->GetParam<ParamMatrix4>(
- Matrix4Composition::kOutputMatrixParamName);
-}
-
-ParamMatrix4* Collada::BuildComposition(const Matrix4& matrix,
- ParamMatrix4* input_matrix) {
- Matrix4Composition* composition = pack_->Create<Matrix4Composition>();
- composition->set_local_matrix(matrix);
- BindParams(composition, Matrix4Composition::kInputMatrixParamName,
- input_matrix);
- return composition->GetParam<ParamMatrix4>(
- Matrix4Composition::kOutputMatrixParamName);
-}
-
-ParamMatrix4* Collada::BuildTranslation(FCDTTranslation* transform,
- ParamMatrix4* input_matrix,
- ParamFloat* animation_input) {
- Matrix4Translation* translation = pack_->Create<Matrix4Translation>();
-
- ParamFloat3* animated_param = translation->GetParam<ParamFloat3>(
- Matrix4Translation::kTranslationParamName);
- Vector3 default_value = FMVector3ToVector3(transform->GetTranslation());
- BuildFloat3Animation(animated_param, transform->GetAnimated(),
- animation_input, Float3(default_value));
-
- BindParams(translation, Matrix4Composition::kInputMatrixParamName,
- input_matrix);
- return translation->GetParam<ParamMatrix4>(
- Matrix4Composition::kOutputMatrixParamName);
-}
-
-ParamMatrix4* Collada::BuildRotation(FCDTRotation* transform,
- ParamMatrix4* input_matrix,
- ParamFloat* animation_input) {
- Matrix4AxisRotation* rotation = pack_->Create<Matrix4AxisRotation>();
-
- ParamFloat3* animated_axis = rotation->GetParam<ParamFloat3>(
- Matrix4AxisRotation::kAxisParamName);
- Vector3 default_axis = FMVector3ToVector3(transform->GetAxis());
- BuildFloat3Animation(animated_axis, transform->GetAnimated(),
- animation_input, Float3(default_axis));
-
- ParamFloat* animated_angle = rotation->GetParam<ParamFloat>(
- Matrix4AxisRotation::kAngleParamName);
- float default_angle = transform->GetAngle();
- BuildFloatAnimation(animated_angle,
- transform->GetAnimated(),
- ".ANGLE",
- animation_input,
- o3d::kPi / 180.0f,
- default_angle);
-
- BindParams(rotation, Matrix4Composition::kInputMatrixParamName, input_matrix);
- return rotation->GetParam<ParamMatrix4>(
- Matrix4Composition::kOutputMatrixParamName);
-}
-
-ParamMatrix4* Collada::BuildScaling(FCDTScale* transform,
- ParamMatrix4* input_matrix,
- ParamFloat* animation_input) {
- Matrix4Scale* scaling = pack_->Create<Matrix4Scale>();
-
- ParamFloat3* animated_param = scaling->GetParam<ParamFloat3>(
- Matrix4Scale::kScaleParamName);
- Vector3 default_value = FMVector3ToVector3(transform->GetScale());
- BuildFloat3Animation(animated_param, transform->GetAnimated(),
- animation_input, Float3(default_value));
-
- BindParams(scaling, Matrix4Composition::kInputMatrixParamName, input_matrix);
- return scaling->GetParam<ParamMatrix4>(
- Matrix4Composition::kOutputMatrixParamName);
-}
-
-// Builds a Transform node corresponding to the transform elements of
-// a given node. All transformations (rotation, translation, scale,
-// etc) are collapsed into a single Transform.
-// Parameters:
-// node: The FCollada node whose transforms are replicated.
-// Return value:
-// The new Transform.
-Transform* Collada::BuildTransform(FCDSceneNode* node,
- Transform* parent_transform,
- ParamFloat* animation_input) {
- const wchar_t* name = node->GetName().c_str();
-
- String name_utf8 = WideToUTF8(name);
-
- Transform* transform = pack_->Create<Transform>();
- transform->set_name(name_utf8);
- transform->SetParent(parent_transform);
-
- bool any_animated = false;
- for (size_t i = 0; i < node->GetTransformCount(); i++) {
- FCDTransform* fcd_transform = node->GetTransform(i);
- any_animated |= fcd_transform->IsAnimated();
- }
-
- if (any_animated) {
- // At least one of the collada transforms is animated so construct the
- // transform hierarchy and connect its output to the local_matrix for
- // this node.
- ParamMatrix4* input_matrix = NULL;
- for (size_t i = 0; i < node->GetTransformCount(); i++) {
- FCDTransform* fcd_transform = node->GetTransform(i);
- switch (fcd_transform->GetType()) {
- case FCDTransform::MATRIX :
- input_matrix = BuildComposition(
- static_cast<FCDTMatrix*>(fcd_transform),
- input_matrix, animation_input);
- break;
- case FCDTransform::TRANSLATION:
- input_matrix = BuildTranslation(
- static_cast<FCDTTranslation*>(fcd_transform),
- input_matrix, animation_input);
- break;
- case FCDTransform::ROTATION:
- input_matrix = BuildRotation(
- static_cast<FCDTRotation*>(fcd_transform),
- input_matrix, animation_input);
- break;
- case FCDTransform::SCALE:
- input_matrix = BuildScaling(
- static_cast<FCDTScale*>(fcd_transform),
- input_matrix, animation_input);
- break;
- default:
- input_matrix = BuildComposition(
- FMMatrix44ToMatrix4(fcd_transform->ToMatrix()),
- input_matrix);
- break;
- }
- }
-
- ParamMatrix4* local_matrix_param = transform->GetParam<ParamMatrix4>(
- Transform::kLocalMatrixParamName);
- local_matrix_param->Bind(input_matrix);
- } else {
- // None of collada transforms are animated so just compute the
- // overall transform and set it as the value of the local_matrix
- // for this node. This saves memory and improves performance but
- // more importantly it will allow JavaScript code to set the value of
- // the local_matrix directly without first having to unbind it.
- Matrix4 local_matrix(Matrix4::identity());
- for (size_t i = 0; i < node->GetTransformCount(); i++) {
- FCDTransform* fcd_transform = node->GetTransform(i);
- local_matrix *= FMMatrix44ToMatrix4(fcd_transform->ToMatrix());
- }
- transform->set_local_matrix(local_matrix);
- }
-
- return transform;
-}
-
-NodeInstance *Collada::CreateInstanceTree(FCDSceneNode *node) {
- NodeInstance *instance = new NodeInstance(node);
- NodeInstance::NodeInstanceList &children = instance->children();
- for (size_t i = 0; i < node->GetChildrenCount(); ++i) {
- FCDSceneNode *child_node = node->GetChild(i);
- NodeInstance *child_instance = CreateInstanceTree(child_node);
- children.push_back(child_instance);
- }
- return instance;
-}
-
-NodeInstance *NodeInstance::FindNodeInTree(FCDSceneNode *node) {
- if (node == node_)
- return this;
- for (unsigned int i = 0; i < children_.size(); ++i) {
- NodeInstance *instance = children_[i]->FindNodeInTree(node);
- if (instance)
- return instance;
- }
- return NULL;
-}
-
-NodeInstance *Collada::FindNodeInstance(FCDSceneNode *node) {
- // First try the fast path, in the case where the node is not instanced more
- // than once.
- NodeInstance *instance = FindNodeInstanceFast(node);
- if (!instance) {
- // If it fails, look in the whole instance tree.
- instance = instance_root_->FindNodeInTree(node);
- }
- return instance;
-}
-
-NodeInstance *Collada::FindNodeInstanceFast(FCDSceneNode *node) {
- if (node == instance_root_->node())
- return instance_root_;
- // If the node is instanced more than once, fail.
- if (node->GetParentCount() != 1)
- return NULL;
- NodeInstance *parent_instance = FindNodeInstanceFast(node->GetParent(0));
- // Look for self in parent's childrens.
- return parent_instance->FindNodeShallow(node);
-}
-
-// Recursively imports a tree of nodes from FCollada, rooted at the
-// given node, into the O3D scene.
-// Parameters:
-// instance: The root NodeInstance from which to import.
-// parent_transform: The parent Transform under which the tree will be placed.
-// animation_input: The float parameter used as the input to the animation
-// (if present). This is usually time. This can be null
-// if there is no animation.
-bool Collada::ImportTree(NodeInstance *instance,
- Transform* parent_transform,
- ParamFloat* animation_input) {
- FCDSceneNode *node = instance->node();
- Transform* transform = BuildTransform(node, parent_transform,
- animation_input);
- instance->set_transform(transform);
-
- // recursively import the rest of the nodes in the tree
- const NodeInstance::NodeInstanceList &children = instance->children();
- for (size_t i = 0; i < children.size(); ++i) {
- if (!ImportTree(children[i], transform, animation_input)) {
- return false;
- }
- }
- return true;
-}
-
-bool Collada::ImportTreeInstances(FCDocument* doc,
- NodeInstance *node_instance) {
- FCDSceneNode *node = node_instance->node();
- // recursively import the rest of the nodes in the tree
- const NodeInstance::NodeInstanceList &children = node_instance->children();
- for (size_t i = 0; i < children.size(); ++i) {
- if (!ImportTreeInstances(doc, children[i])) {
- return false;
- }
- }
-
- Transform* transform = node_instance->transform();
- for (size_t i = 0; i < node->GetInstanceCount(); ++i) {
- FCDEntityInstance* instance = node->GetInstance(i);
- FCDCamera* camera(NULL);
- FCDGeometryInstance* geom_instance(NULL);
-
- LOG_ASSERT(instance != 0);
- // Import each node based on what kind of entity it is
- // TODO(o3d): add more entity types as they are supported
- switch (instance->GetEntityType()) {
- case FCDEntity::CAMERA:
- // camera entity
- camera = down_cast<FCDCamera*>(instance->GetEntity());
- BuildCamera(doc, camera, transform, node);
- break;
- case FCDEntity::GEOMETRY: {
- // geometry entity
- geom_instance = static_cast<FCDGeometryInstance*>(instance);
- Shape* shape = GetShape(doc, geom_instance);
- if (shape) {
- transform->AddShape(shape);
- }
- break;
- }
- case FCDEntity::CONTROLLER: {
- FCDControllerInstance* controller_instance =
- static_cast<FCDControllerInstance*> (instance);
- FCDController* controller =
- static_cast<FCDController*>(controller_instance->GetEntity());
- if (controller) {
- if (controller->IsSkin()) {
- Shape* shape = GetSkinnedShape(doc,
- controller_instance,
- node_instance,
- transform);
- if (shape) {
- transform->AddShape(shape);
- } else {
- return false;
- }
- }
- }
- break;
- }
- default:
- // do nothing
- break;
- }
- }
- return true;
-}
-
-// Converts an FCollada vertex attribute semantic into an O3D
-// vertex attribute semantic. Returns the O3D semantic, or
-// UNKNOWN_SEMANTIC on failure.
-static Stream::Semantic C2G3DSemantic(FUDaeGeometryInput::Semantic semantic) {
- switch (semantic) {
- case FUDaeGeometryInput::POSITION: return Stream::POSITION;
- case FUDaeGeometryInput::VERTEX: return Stream::POSITION;
- case FUDaeGeometryInput::NORMAL: return Stream::NORMAL;
- case FUDaeGeometryInput::TEXTANGENT: return Stream::TANGENT;
- case FUDaeGeometryInput::TEXBINORMAL: return Stream::BINORMAL;
- case FUDaeGeometryInput::TEXCOORD: return Stream::TEXCOORD;
- case FUDaeGeometryInput::COLOR: return Stream::COLOR;
- default: return Stream::UNKNOWN_SEMANTIC;
- }
-}
-
-// Imports information from a Collada camera node and places it in Params of
-// the transform corresponding to the node it's parented under.
-// Parameters:
-// doc: The FCollada document from which to import nodes.
-// camera: The FCDCamera node in question
-// transform: The O3D Transform on which the camera parameters
-// will be created. It cannot be NULL.
-// parent_node: The FCDSceneNode that the parent transform was created
-// from. This node is used to extract possible LookAt
-// elements. It cannot be NULL.
-void Collada::BuildCamera(FCDocument* doc,
- FCDCamera* camera,
- Transform* transform,
- FCDSceneNode* parent_node) {
- LOG_ASSERT(doc && camera && transform && parent_node);
-
- // Create a transform node for the camera
- String camera_name = WideToUTF8(camera->GetName().c_str());
-
- // Tag this node as a camera
- ParamString* param_tag = transform->CreateParam<ParamString>(
- COLLADA_STRING_CONSTANT("tags"));
-
- // Add more tags when required
- param_tag->set_value("camera");
-
- // Create the rest of the params
-
- // Projection type: either 'orthographic' or 'perspective'
- ParamString* param_proj_type =
- transform->CreateParam<ParamString>(
- COLLADA_STRING_CONSTANT("projectionType"));
-
- // Aspect ratio
- float camera_aspect_ratio;
- ParamFloat* param_proj_aspect_ratio =
- transform->CreateParam<ParamFloat>(
- COLLADA_STRING_CONSTANT("projectionAspectRatio"));
-
- // Near/far z-planes
- float camera_near_z, camera_far_z;
- ParamFloat* param_proj_nearz =
- transform->CreateParam<ParamFloat>(
- COLLADA_STRING_CONSTANT("projectionNearZ"));
- ParamFloat* param_proj_farz =
- transform->CreateParam<ParamFloat>(
- COLLADA_STRING_CONSTANT("projectionFarZ"));
-
- // Calculate shared params
- camera_near_z = camera->GetNearZ();
- camera_far_z = camera->GetFarZ();
- param_proj_nearz->set_value(camera_near_z);
- param_proj_farz->set_value(camera_far_z);
-
- if (camera->GetProjectionType() == FCDCamera::ORTHOGRAPHIC) {
- // Orthographic projection
- param_proj_type->set_value("orthographic");
-
- // Additional params
- // Horizontal and vertical magnifications
- ParamFloat* param_proj_mag_x =
- transform->CreateParam<ParamFloat>(
- COLLADA_STRING_CONSTANT("projectionMagX"));
- ParamFloat* param_proj_mag_y =
- transform->CreateParam<ParamFloat>(
- COLLADA_STRING_CONSTANT("projectionMagY"));
-
- // Find aspect ratio and magnifications
- float camera_mag_x, camera_mag_y;
- if (camera->HasHorizontalMag() && camera->HasVerticalMag())
- camera_aspect_ratio = camera->GetMagY() / camera->GetMagX();
- else
- camera_aspect_ratio = camera->GetAspectRatio();
-
- if (camera->HasHorizontalMag())
- camera_mag_x = camera->GetMagX();
- else
- camera_mag_x = camera->GetMagY() * camera_aspect_ratio;
-
- if (camera->HasVerticalMag())
- camera_mag_y = camera->GetMagY();
- else
- camera_mag_y = camera->GetMagX() / camera_aspect_ratio;
-
- // Set the values of the additional params
- param_proj_mag_x->set_value(camera_mag_x);
- param_proj_mag_y->set_value(camera_mag_y);
- param_proj_aspect_ratio->set_value(camera_aspect_ratio);
- } else if (camera->GetProjectionType() == FCDCamera::PERSPECTIVE) {
- // Perspective projection
- param_proj_type->set_value("perspective");
-
- // Additional params
- // Vertical field of view
- ParamFloat* param_proj_fov_y =
- transform->CreateParam<ParamFloat>(
- COLLADA_STRING_CONSTANT("perspectiveFovY"));
-
- // Find aspect ratio and vertical FOV
- float camera_fov_y;
- if (camera->HasHorizontalFov() && camera->HasVerticalFov())
- camera_aspect_ratio = camera->GetFovY() / camera->GetFovX();
- else
- camera_aspect_ratio = camera->GetAspectRatio();
-
- if (camera->HasVerticalFov())
- camera_fov_y = camera->GetFovY();
- else
- camera_fov_y = camera->GetFovX() / camera_aspect_ratio;
-
- // Set the values of the additional params
- param_proj_fov_y->set_value(camera_fov_y);
- param_proj_aspect_ratio->set_value(camera_aspect_ratio);
- }
-
- // Search the FCDSceneNode for a LookAt element, extract the eye, target,
- // and up values and store them as Params on the Transform. If multiple
- // LookAt elements are defined under the parent node, we only pick the first
- // one.
- for (size_t i = 0; i < parent_node->GetTransformCount(); i++) {
- FCDTransform* transform_object = parent_node->GetTransform(i);
- if (transform_object->GetType() == FCDTransform::LOOKAT) {
- FCDTLookAt* look_at = static_cast<FCDTLookAt*>(transform_object);
- FMVector3 position = look_at->GetPosition();
- FMVector3 target = look_at->GetTarget();
- FMVector3 up = look_at->GetUp();
-
- // Get the world matrix of the transform above the camera transform.
- // We use this value to transform the eye, target and up to the world
- // coordinate system so that they can be used directly to make a camera
- // view matrix.
- Matrix4 parent_world = Matrix4::identity();
- if (transform->parent())
- parent_world = transform->parent()->GetUpdatedWorldMatrix();
-
- ParamFloat3* param_eye_position =
- transform->CreateParam<ParamFloat3>(
- COLLADA_STRING_CONSTANT("eyePosition"));
- Vector4 world_eye = parent_world * Point3(position.x,
- position.y,
- position.z);
- param_eye_position->set_value(Float3(world_eye.getX(),
- world_eye.getY(),
- world_eye.getZ()));
-
- ParamFloat3* param_target_position =
- transform->CreateParam<ParamFloat3>(
- COLLADA_STRING_CONSTANT("targetPosition"));
- Vector4 world_target = parent_world * Point3(target.x,
- target.y,
- target.z);
- param_target_position->set_value(Float3(world_target.getX(),
- world_target.getY(),
- world_target.getZ()));
-
- ParamFloat3* param_up_vector =
- transform->CreateParam<ParamFloat3>(
- COLLADA_STRING_CONSTANT("upVector"));
- Vector4 world_up = parent_world * Vector4(up.x, up.y, up.z, 0.0f);
- param_up_vector->set_value(Float3(world_up.getX(),
- world_up.getY(),
- world_up.getZ()));
-
- break;
- }
- }
-}
-
-Shape* Collada::GetShape(FCDocument* doc,
- FCDGeometryInstance* geom_instance) {
- Shape* shape = NULL;
- FCDGeometry* geom = static_cast<FCDGeometry*>(geom_instance->GetEntity());
- if (geom) {
- fm::string geom_id = geom->GetDaeId();
- shape = shapes_[geom_id.c_str()];
- if (!shape) {
- shape = BuildShape(doc, geom_instance, geom, NULL);
- if (!shape)
- return NULL;
- shapes_[geom_id.c_str()] = shape;
- }
- }
- return shape;
-}
-
-Shape* Collada::GetSkinnedShape(FCDocument* doc,
- FCDControllerInstance* instance,
- NodeInstance *parent_node_instance,
- Transform* parent) {
- Shape* shape = NULL;
- FCDController* controller =
- static_cast<FCDController*>(instance->GetEntity());
- if (controller && controller->IsSkin()) {
- fm::string id = controller->GetDaeId();
- shape = skinned_shapes_[id.c_str()];
- if (!shape) {
- shape = BuildSkinnedShape(doc, instance, parent_node_instance, parent);
- if (!shape)
- return NULL;
- skinned_shapes_[id.c_str()] = shape;
- }
- }
- return shape;
-}
-
-// Builds an O3D shape node corresponding to a given FCollada geometry
-// instance.
-Shape* Collada::BuildShape(FCDocument* doc,
- FCDGeometryInstance* geom_instance,
- FCDGeometry* geom,
- TranslationMap* translationMap) {
- Shape* shape = NULL;
- LOG_ASSERT(doc && geom_instance && geom);
- if (geom && geom->IsMesh()) {
- String geom_name = WideToUTF8(geom->GetName().c_str());
- shape = pack_->Create<Shape>();
- shape->set_name(geom_name);
- FCDGeometryMesh* mesh = geom->GetMesh();
- LOG_ASSERT(mesh);
- FCDGeometryPolygonsTools::Triangulate(mesh);
- FCDGeometryPolygonsTools::GenerateUniqueIndices(mesh, NULL, translationMap);
- size_t num_polygons = mesh->GetPolygonsCount();
- size_t num_indices = mesh->GetFaceVertexCount();
- if (num_polygons <= 0 || num_indices <= 0) return NULL;
- FCDGeometrySource* pos_source =
- mesh->FindSourceByType(FUDaeGeometryInput::POSITION);
- if (pos_source == NULL) return NULL;
- size_t num_vertices = pos_source->GetValueCount();
- size_t num_sources = mesh->GetSourceCount();
-
- // Create vertex streams corresponding to the COLLADA sources.
- // These streams are common to all polygon sets in this mesh.
- // The BuildSkinnedShape code assumes this so if you change it you'll need
- // to fix BuildSkinnedShape.
- StreamBank* stream_bank = pack_->Create<StreamBank>();
- stream_bank->set_name(geom_name);
-
- int semantic_counts[Stream::TEXCOORD + 1] = { 0, };
-
- scoped_array<Field*> fields(new Field*[num_sources]);
-
- VertexBuffer* vertex_buffer = pack_->Create<VertexBuffer>();
- vertex_buffer->set_name(geom_name);
-
- // first create all the fields.
- for (size_t s = 0; s < num_sources; ++s) {
- FCDGeometrySource* source = mesh->GetSource(s);
- LOG_ASSERT(source);
- Stream::Semantic semantic = C2G3DSemantic(source->GetType());
- LOG_ASSERT(semantic <= Stream::TEXCOORD);
- if (semantic == Stream::UNKNOWN_SEMANTIC) continue;
-
- // The call to GenerateUniqueIndices() above should have made
- // all sources the same length.
- LOG_ASSERT(source->GetValueCount() == num_vertices);
-
- int stride = source->GetStride();
- if (semantic == Stream::COLOR && stride == 4) {
- fields[s] = vertex_buffer->CreateField(UByteNField::GetApparentClass(),
- stride);
- } else {
- fields[s] = vertex_buffer->CreateField(FloatField::GetApparentClass(),
- stride);
- }
- }
-
- if (!vertex_buffer->AllocateElements(num_vertices)) {
- O3D_ERROR(service_locator_) << "Failed to allocate vertex buffer";
- return NULL;
- }
-
- for (size_t s = 0; s < num_sources; ++s) {
- FCDGeometrySource* source = mesh->GetSource(s);
- LOG_ASSERT(source);
- Stream::Semantic semantic = C2G3DSemantic(source->GetType());
- LOG_ASSERT(semantic <= Stream::TEXCOORD);
- if (semantic == Stream::UNKNOWN_SEMANTIC) continue;
- int stride = source->GetStride();
-
- const float* source_data = source->GetData();
- if (semantic == Stream::TANGENT || semantic == Stream::BINORMAL) {
- // FCollada uses the convention that the tangent points
- // along -u and the binormal along -v in model space.
- // Convert to the more common convention where the tangent
- // points along +u and the binormal along +v. This is what, for
- // example, tools that convert height maps to normal maps tend to
- // assume. It is also what the O3D shaders assume.
- size_t num_values = source->GetDataCount();
- scoped_array<float>values(new float[num_values]);
- for (size_t i = 0; i < num_values ; ++i) {
- values[i] = -source_data[i];
- }
- fields[s]->SetFromFloats(&values[0], stride, 0, num_vertices);
- } else {
- fields[s]->SetFromFloats(source_data, stride, 0, num_vertices);
- }
-
- stream_bank->SetVertexStream(semantic, semantic_counts[semantic],
- fields[s], 0);
- // NOTE: This doesn't really seem like the correct thing to do but I'm not
- // sure we have enough info to do the correct thing. The issue is we
- // need to connect these streams to the shader, the shader needs to know
- // which streams go with which varying parameters but for the standard
- // collada materials I don't think any such information is available.
- ++semantic_counts[semantic];
- }
-
- for (size_t p = 0; p < num_polygons; ++p) {
- FCDGeometryPolygons* polys = mesh->GetPolygons(p);
- FCDGeometryPolygonsInput* input = polys->GetInput(0);
-
- size_t size = input->GetIndexCount();
- size_t vertices_per_primitive = 0;
- Primitive::PrimitiveType primitive_type;
- switch (polys->GetPrimitiveType()) {
- case FCDGeometryPolygons::POLYGONS:
- vertices_per_primitive = 3;
- primitive_type = Primitive::TRIANGLELIST;
- break;
- case FCDGeometryPolygons::LINES:
- vertices_per_primitive = 2;
- primitive_type = Primitive::LINELIST;
- break;
- default:
- // unsupported geometry type; skip it
- continue;
- }
-
- // If there are no vertices, don't make this primitive.
- if (size == 0) continue;
-
- // If we don't have a multiple of the verts-per-primitive, bail now.
- if (size % vertices_per_primitive != 0) {
- O3D_ERROR(service_locator_) << "Geometry \"" << geom_name
- << "\" contains " << size
- << " vertices, which is not a multiple of "
- << vertices_per_primitive << "; skipped";
- continue;
- }
-
- // Get the material for this polygon set.
- Material* material = NULL;
- FCDMaterialInstance* mat_instance = geom_instance->FindMaterialInstance(
- polys->GetMaterialSemantic());
- if (mat_instance) {
- FCDMaterial* collada_material = mat_instance->GetMaterial();
- material = BuildMaterial(doc, collada_material);
- }
- if (!material) {
- material = GetDummyMaterial();
- }
- // Create an index buffer for this group of polygons.
-
- String primitive_name(geom_name + "|" + material->name());
-
- IndexBuffer* indexBuffer = pack_->Create<IndexBuffer>();
- indexBuffer->set_name(primitive_name);
- if (!indexBuffer->AllocateElements(size)) {
- O3D_ERROR(service_locator_) << "Failed to allocate index buffer.";
- return NULL;
- }
- indexBuffer->index_field()->SetFromUInt32s(input->GetIndices(), 1, 0,
- size);
-
- // Create a primitive for this group of polygons.
- Primitive* primitive = pack_->Create<Primitive>();
- primitive->set_name(primitive_name);
-
- primitive->set_material(material);
-
- primitive->SetOwner(shape);
- primitive->set_primitive_type(primitive_type);
- size_t num_prims = size / vertices_per_primitive;
- primitive->set_number_primitives(static_cast<unsigned int>(num_prims));
- primitive->set_number_vertices(static_cast<unsigned int>(num_vertices));
-
- // Set the index buffer for this primitive.
- primitive->set_index_buffer(indexBuffer);
-
- // Set the vertex streams for this primitive to the common set for this
- // mesh.
- primitive->set_stream_bank(stream_bank);
- }
- }
- return shape;
-}
-
-Shape* Collada::BuildSkinnedShape(FCDocument* doc,
- FCDControllerInstance* instance,
- NodeInstance *parent_node_instance,
- Transform* parent) {
- // TODO(o3d): Handle chained controllers. Morph->Skin->...
- // TODO(gman): Change this to correctly create the skin, separate from
- // ParamArray and SkinEval so that we can support instanced skins.
- Shape* shape = NULL;
- LOG_ASSERT(doc && instance);
- FCDController* controller =
- static_cast<FCDController*>(instance->GetEntity());
- if (controller && controller->IsSkin()) {
- FCDSkinController* skin_controller = controller->GetSkinController();
- TranslationMap translationMap;
- shape = BuildShape(doc,
- instance,
- controller->GetBaseGeometry(),
- &translationMap);
- if (!shape) {
- return NULL;
- }
-
- // Convert the translation table to new->old map.
- size_t num_vertices = 0;
- std::vector<unsigned> new_to_old_indices;
- {
- // walk the map once to figure out the number of vertices.
- TranslationMap::iterator end = translationMap.end();
- for (TranslationMap::iterator it = translationMap.begin();
- it != end;
- ++it) {
- num_vertices += it->second.size();
- }
-
- // Init our array to UINT_MAX so we can check for collisions
- new_to_old_indices.resize(num_vertices, UINT_MAX);
-
- // walk the map again and fill out our remap table.
- for (TranslationMap::iterator it = translationMap.begin();
- it != end;
- ++it) {
- const UInt32List& intlist = it->second;
- for (size_t gg = 0; gg < intlist.size(); ++gg) {
- unsigned new_index = intlist[gg];
- LOG_ASSERT(new_to_old_indices.at(new_index) == UINT_MAX);
- new_to_old_indices[new_index] = it->first;
- }
- }
- }
-
- // There's a BIG assumption here. We assume the first primitive on the shape
- // has vertex buffers that are shared on all the primitives under this shape
- // such that we only need to copy the first primitive's vertex buffers to
- // skin everything. This is actually what was BuildShape was doing at the
- // time this code was written.
- const ElementRefArray& elements = shape->GetElementRefs();
- if (elements.empty()) {
- return NULL;
- }
- // check that they all use the same StreamBank and are all Primitives
- for (unsigned ii = 0; ii < elements.size(); ++ii) {
- if (!elements[ii]->IsA(Primitive::GetApparentClass())) {
- O3D_ERROR(service_locator_)
- << "Element in Shape '" << shape->name() << "' is not a Primitive.";
- return NULL;
- }
- if (down_cast<Primitive*>(elements[ii].Get())->stream_bank() !=
- down_cast<Primitive*>(elements[0].Get())->stream_bank()) {
- O3D_ERROR(service_locator_)
- << "More than one StreamBank in Shape '" << shape->name() << "'.";
- return NULL;
- }
- }
- Primitive* primitive = down_cast<Primitive*>(elements[0].Get());
-
- String controller_name = WideToUTF8(controller->GetName().c_str());
-
- ParamArray* matrices = pack_->Create<ParamArray>();
- Skin* skin = pack_->Create<Skin>();
- skin->set_name(controller_name);
- SkinEval* skin_eval = pack_->Create<SkinEval>();
- skin_eval->set_name(controller_name);
-
- skin_eval->set_skin(skin);
- skin_eval->set_matrices(matrices);
- skin_eval->GetParam<ParamMatrix4>(SkinEval::kBaseParamName)->Bind(
- parent->GetParam<ParamMatrix4>(Transform::kWorldMatrixParamName));
-
- // Bind bones to matrices
- size_t num_bones = instance->GetJointCount();
- if (num_bones > 0) {
- matrices->CreateParam<ParamMatrix4>(num_bones - 1);
- for (size_t ii = 0; ii < num_bones; ++ii) {
- FCDSceneNode* node = instance->GetJoint(ii);
- LOG_ASSERT(node);
- // Note: in case of instancing, the intended instance is ill-defined,
- // but that is a problem in the Collada document itself. So we'll assume
- // the file is somewhat well defined.
- // First we try the single instance case.
- NodeInstance *node_instance = FindNodeInstanceFast(node);
- if (!node_instance) {
- // Second we try nodes underneath the same parent as the controller
- // instance. Max and Maya seem to do that.
- node_instance = parent_node_instance->FindNodeInTree(node);
- }
- if (!node_instance) {
- // Third we try in the entire tree.
- node_instance = instance_root_->FindNodeInTree(node);
- }
- if (!node_instance) {
- String bone_name = WideToUTF8(node->GetName().c_str());
- O3D_ERROR(service_locator_)
- << "Could not find node instance for bone " << bone_name.c_str();
- continue;
- }
- Transform* bone = node_instance->transform();
- LOG_ASSERT(bone);
- matrices->GetUntypedParam(ii)->Bind(
- bone->GetUntypedParam(Transform::kWorldMatrixParamName));
- }
- }
-
- Matrix4 bind_shape_matrix(
- FMMatrix44ToMatrix4(skin_controller->GetBindShapeTransform()));
- Matrix4 inverse_bind_shape_matrix(inverse(bind_shape_matrix));
-
- // Get the bind pose inverse matrices
- LOG_ASSERT(num_bones == skin_controller->GetJointCount());
- for (size_t ii = 0; ii < num_bones; ++ii) {
- FCDSkinControllerJoint* joint = skin_controller->GetJoint(ii);
- skin->SetInverseBindPoseMatrix(
- ii, FMMatrix44ToMatrix4(joint->GetBindPoseInverse()));
- }
-
- // Get Influences.
- for (size_t ii = 0; ii < num_vertices; ++ii) {
- unsigned old_index = new_to_old_indices[ii];
- FCDSkinControllerVertex* vertex =
- skin_controller->GetVertexInfluence(old_index);
- Skin::Influences influences;
- size_t num_influences = vertex->GetPairCount();
- for (size_t jj = 0; jj < num_influences; ++jj) {
- FCDJointWeightPair* weight_pair = vertex->GetPair(jj);
- influences.push_back(Skin::Influence(weight_pair->jointIndex,
- weight_pair->weight));
- }
- skin->SetVertexInfluences(ii, influences);
- }
-
- Matrix4 matrix(bind_shape_matrix);
-
- // Copy shape->primitive buffers.
- // Here we need to also split the original vertex buffer. The issue is
- // the original VertexBuffer might contain POSITION, NORMAL, TEXCOORD,
- // COLOR. Of those, only POSITION and NORMAL are copied to the SourceBuffer.
- // The VertexBuffer still contains POSITON, NORMAL, TEXCOORD, and COLOR so
- // two issues come up
- //
- // 1) If we serialize that VertexBuffer, POSITION and NORMAL are stored
- // twice. Once in the SourceBuffer, again in the VertexBuffer. That's a lot
- // of data to download just to throw it away.
- //
- // 2) If we want to instance the skin we'll need to make a new VertexBuffer
- // so we can store the skinned vertices for the second instance. But we'd
- // like to share the COLOR and TEXCOORDS. To do that they need to be in
- // a separate VertexBuffer.
- StreamBank* old_stream_bank = primitive->stream_bank();
- StreamBank* new_stream_bank = pack_->Create<StreamBank>();
- new_stream_bank->set_name(String("skinned_") + old_stream_bank->name());
- Buffer* old_buffer = NULL;
- SourceBuffer* source_buffer = pack_->Create<SourceBuffer>();
- VertexBuffer* shared_buffer = pack_->Create<VertexBuffer>();
- DestinationBuffer* dest_buffer = pack_->Create<DestinationBuffer>();
- const StreamParamVector& source_stream_params =
- old_stream_bank->vertex_stream_params();
- std::vector<Field*> source_fields(source_stream_params.size(), NULL);
- std::vector<Field*> dest_fields(source_stream_params.size(), NULL);
- // first make all the fields.
- for (unsigned ii = 0; ii < source_stream_params.size(); ++ii) {
- const Stream& source_stream = source_stream_params[ii]->stream();
- const Field& field = source_stream.field();
- if (old_buffer == NULL) {
- old_buffer = field.buffer();
- } else if (old_buffer != field.buffer()) {
- O3D_ERROR(service_locator_)
- << "More than 1 buffer used by StreamBank '"
- << old_stream_bank->name().c_str()
- << "' which the collada importer does not currently support";
- }
- bool copied = false;
- if (field.IsA(FloatField::GetApparentClass()) &&
- (field.num_components() == 3 ||
- field.num_components() == 4)) {
- switch (source_stream.semantic()) {
- case Stream::POSITION:
- case Stream::NORMAL:
- case Stream::BINORMAL:
- case Stream::TANGENT: {
- copied = true;
- unsigned num_source_components = field.num_components();
- unsigned num_source_vertices = source_stream.GetMaxVertices();
- if (num_source_vertices != num_vertices) {
- O3D_ERROR(service_locator_)
- << "Number of vertices in stream_bank '"
- << old_stream_bank->name().c_str()
- << "' does not equal the number of vertices in the Skin '"
- << skin->name().c_str() << "'";
- return NULL;
- }
- source_fields[ii] = source_buffer->CreateField(
- FloatField::GetApparentClass(), num_source_components);
- DCHECK(source_fields[ii]);
- dest_fields[ii] = dest_buffer->CreateField(
- FloatField::GetApparentClass(), num_source_components);
- DCHECK(dest_fields[ii]);
- if (!new_stream_bank->SetVertexStream(
- source_stream.semantic(),
- source_stream.semantic_index(),
- dest_fields[ii],
- 0)) {
- O3D_ERROR(service_locator_)
- << "could not SetVertexStream on StreamBank '"
- << new_stream_bank->name() << "'";
- return NULL;
- }
- }
- default:
- // do nothing
- break;
- }
- }
- if (!copied) {
- // It's a shared field, copy it to the shared buffer.
- source_fields[ii] = shared_buffer->CreateField(field.GetClass(),
- field.num_components());
- new_stream_bank->SetVertexStream(source_stream.semantic(),
- source_stream.semantic_index(),
- source_fields[ii],
- 0);
- }
- }
-
- if (!source_buffer->AllocateElements(num_vertices) ||
- !shared_buffer->AllocateElements(num_vertices) ||
- !dest_buffer->AllocateElements(num_vertices)) {
- O3D_ERROR(service_locator_)
- << "Failed to allocate destination vertex buffer";
- return NULL;
- }
-
- for (unsigned ii = 0; ii < source_stream_params.size(); ++ii) {
- const Stream& source_stream = source_stream_params[ii]->stream();
- const Field& field = source_stream.field();
- bool copied = false;
- if (field.IsA(FloatField::GetApparentClass()) &&
- (field.num_components() == 3 ||
- field.num_components() == 4)) {
- switch (source_stream.semantic()) {
- case Stream::POSITION:
- case Stream::NORMAL:
- case Stream::BINORMAL:
- case Stream::TANGENT: {
- copied = true;
- unsigned num_source_components = field.num_components();
- Field* source_field = source_fields[ii];
-
- std::vector<float> data(num_vertices * num_source_components);
- field.GetAsFloats(0, &data[0], num_source_components,
- num_vertices);
- // TODO(o3d): Remove this matrix multiply. I don't think it is
- // needed.
- for (unsigned vv = 0; vv < num_vertices; ++vv) {
- float* values = &data[vv * num_source_components];
- switch (field.num_components()) {
- case 3: {
- if (source_stream.semantic() == Stream::POSITION) {
- Vector4 result(matrix * Point3(values[0],
- values[1],
- values[2]));
- values[0] = result.getElem(0);
- values[1] = result.getElem(1);
- values[2] = result.getElem(2);
- } else {
- Vector4 result(matrix * Vector3(values[0],
- values[1],
- values[2]));
- values[0] = result.getElem(0);
- values[1] = result.getElem(1);
- values[2] = result.getElem(2);
- }
- break;
- }
- case 4: {
- Vector4 result(matrix * Vector4(values[0],
- values[1],
- values[2],
- values[3]));
- values[0] = result.getElem(0);
- values[1] = result.getElem(1);
- values[2] = result.getElem(2);
- values[3] = result.getElem(3);
- break;
- }
- }
- }
- source_field->SetFromFloats(&data[0], num_source_components, 0,
- num_vertices);
- // Bind streams
- skin_eval->SetVertexStream(source_stream.semantic(),
- source_stream.semantic_index(),
- source_field,
- 0);
- new_stream_bank->BindStream(skin_eval,
- source_stream.semantic(),
- source_stream.semantic_index());
- break;
- }
- default:
- // do nothing
- break;
- }
- }
- if (!copied) {
- Field* source_field = source_fields[ii];
- source_field->Copy(field);
- }
- }
-
- // Set all primitives to use new stream bank.
- for (unsigned ii = 0; ii < elements.size(); ++ii) {
- down_cast<Primitive*>(elements[ii].Get())->set_stream_bank(
- new_stream_bank);
- }
- pack_->RemoveObject(old_stream_bank);
- if (old_buffer) {
- source_buffer->set_name(String("source_") + old_buffer->name());
- dest_buffer->set_name(String("skinned_") + old_buffer->name());
- shared_buffer->set_name(String("shared_") + old_buffer->name());
- pack_->RemoveObject(old_buffer);
- }
- }
- return shape;
-}
-
-Texture* Collada::BuildTextureFromImage(FCDImage* image) {
- const fstring filename = image->GetFilename();
- Texture* tex = textures_[filename.c_str()];
- if (!tex) {
- FilePath file_path = WideToFilePath(filename.c_str());
- FilePath uri = file_path;
-
- std::string tempfile;
- if (collada_zip_archive_) {
- // If we're getting data from a zip archive, then we just strip
- // the "/" from the beginning of the name, since that represents
- // the root of the archive, and we can assume all the paths in
- // the archive are relative to that.
- if (uri.value()[0] == FILE_PATH_LITERAL('/')) {
- uri = FilePath(uri.value().substr(1));
- }
- // NOTE: We have the opportunity to simply extract a memory
- // buffer for the image data here, but currently the image loaders expect
- // to read a file, so we write out a temp file...
-
- // filename_utf8 points to the name of the file inside the archive
- // (it doesn't actually live on the filesystem so we make a temp file)
- if (collada_zip_archive_->GetTempFileFromFile(FilePathToUTF8(file_path),
- &tempfile)) {
- file_path = UTF8ToFilePath(tempfile);
- }
- } else {
- GetRelativePathIfPossible(base_path_, uri, &uri);
- }
-
- if (!FindFile(options_.file_paths, file_path, &file_path)) {
- O3D_ERROR(service_locator_) << "Could not find file: " << filename;
- return NULL;
- }
-
- tex = Texture::Ref(
- pack_->CreateTextureFromFile(FilePathToUTF8(uri),
- file_path,
- image::UNKNOWN,
- options_.generate_mipmaps));
- if (tex) {
- const fstring name(image->GetName());
- tex->set_name(WideToUTF8(name.c_str()));
- }
-
- bool inserted_original_data = false;
- bool is_dds = uri.MatchesExtension(UTF8ToFilePathStringType(".dds"));
-
- if (is_dds &&
- options_.convert_dds_to_png &&
- options_.keep_original_data) {
- // The Texture stubs used by the converter don't have a working
- // PlatformSpecificLock, so we need to reload the images using
- // the Bitmap class. We also need to do the DXTn decompression
- // on the CPU because D3D wouldn't provide access to the
- // decompressed data anyway.
-
- // These need to match the order of TextureCUBE::CubeFace.
- static const char* cube_suffixes[6] = {
- "_posx", "_negx", "_posy", "_negy", "_posz", "_negz"
- };
- static const char* cube_prefixes[6] = {
- "posx_", "negx_", "posy_", "negy_", "posz_", "negz_"
- };
-
- BitmapRefArray bitmaps;
- bool is_cube_map = false;
- if (Bitmap::LoadFromFile(service_locator_, file_path,
- image::UNKNOWN, &bitmaps)) {
- is_cube_map = bitmaps.size() == 6;
- for (unsigned int i = 0; i < bitmaps.size(); i++) {
- bool is_compressed =
- (tex->format() == Texture::DXT1 ||
- tex->format() == Texture::DXT3 ||
- tex->format() == Texture::DXT5);
- Bitmap::Ref src_bitmap = bitmaps[i];
- int pitch = src_bitmap->GetMipPitch(0);
- if (is_compressed) {
- // TODO(kbr): there is a bug somewhere in
- // Bitmap::GetMipPitch for compressed textures where its
- // result is off by a factor of two, at least for DXT1
- // textures. Don't have time to debug it right now.
- pitch /= 2;
- }
- uint8* data = src_bitmap->GetMipData(0);
- int width = src_bitmap->width();
- int height = src_bitmap->height();
- int row_width = width * 4;
- int decompressed_size = width * height * 4;
- scoped_array<uint8> decompressed_data(new uint8[decompressed_size]);
- memset(decompressed_data.get(), 0, decompressed_size);
- if (is_compressed) {
- for (int src_y = 0; src_y < height; src_y++) {
- int dest_y = src_y;
- if (is_cube_map) {
- dest_y = height - src_y - 1;
- }
- for (int x = 0; x < width; x++) {
- uint8* ptr =
- &decompressed_data.get()[row_width * dest_y + 4 * x];
- switch (src_bitmap->format()) {
- case Texture::DXT1: {
- fetch_2d_texel_rgba_dxt1(pitch, data, x, src_y, ptr);
- break;
- }
- case Texture::DXT3: {
- fetch_2d_texel_rgba_dxt3(pitch, data, x, src_y, ptr);
- break;
- }
- case Texture::DXT5: {
- fetch_2d_texel_rgba_dxt5(pitch, data, x, src_y, ptr);
- break;
- }
- default:
- DLOG(ERROR) << "Unsupported DDS compressed texture format "
- << src_bitmap->format();
- break;
- }
- // Need to swap the red and blue channels.
- std::swap(ptr[0], ptr[2]);
- }
- }
- } else if (src_bitmap->format() == Texture::XRGB8 ||
- src_bitmap->format() == Texture::ARGB8) {
- for (int src_y = 0; src_y < height; src_y++) {
- int dest_y = src_y;
- if (is_cube_map) {
- dest_y = height - src_y - 1;
- }
- memcpy(decompressed_data.get() + row_width * dest_y,
- data + pitch * src_y,
- row_width);
- }
- } else {
- DLOG(ERROR) << "Unsupported DDS uncompressed texture format "
- << src_bitmap->format();
- return NULL;
- }
- Bitmap::Ref bitmap(new Bitmap(service_locator_));
- bitmap->Allocate(Texture::ARGB8,
- width,
- height,
- 1,
- Bitmap::IMAGE);
- bitmap->SetRect(0, 0, 0, width, height,
- decompressed_data.get(),
- row_width);
- std::vector<uint8> png_data;
- if (!bitmap->WriteToPNGStream(&png_data)) {
- DLOG(ERROR) << "Error writing PNG file for cube map";
- }
- FilePath png_uri = uri;
- if (is_cube_map) {
- png_uri = png_uri.InsertBeforeExtensionASCII(cube_suffixes[i]);
- }
- png_uri = png_uri.ReplaceExtension(UTF8ToFilePathStringType(".png"));
- std::string contents;
- contents.append(reinterpret_cast<char*>(&png_data.front()),
- png_data.size());
- original_data_map_.AddData(png_uri, contents, service_locator_);
- inserted_original_data = true;
-
- // We need to rewrite the o3d.uri param in the Texture as
- // well. For cube map textures, we insert six params named
- // "o3d.negx_uri", etc. and remove the "o3d.uri" param.
- ParamString* uri_param = NULL;
- if (is_cube_map) {
- String name(O3D_STRING_CONSTANT(""));
- name = name.append(cube_prefixes[i]).append("uri");
- uri_param = tex->CreateParam<ParamString>(name);
- } else {
- uri_param = tex->GetParam<ParamString>(O3D_STRING_CONSTANT("uri"));
- }
- DCHECK(uri_param != NULL);
- uri_param->set_value(FilePathToUTF8(png_uri));
- }
- }
- if (is_cube_map) {
- ParamString* uri_param =
- tex->GetParam<ParamString>(O3D_STRING_CONSTANT("uri"));
- DCHECK(uri_param != NULL);
- tex->RemoveParam(uri_param);
- }
- }
-
- if (options_.keep_original_data && !inserted_original_data) {
- // Cache the original data by URI so we can recover it later.
- std::string contents;
- file_util::ReadFileToString(file_path, &contents);
- original_data_map_.AddData(uri, contents, service_locator_);
- }
-
- if (tempfile.size() > 0) ZipArchive::DeleteFile(tempfile);
- textures_[filename.c_str()] = tex;
- }
- return tex;
-}
-
-Texture* Collada::BuildTexture(FCDEffectParameterSurface* surface) {
- // Create the texture.
- Texture* tex = NULL;
- if (surface->GetImageCount() > 0) {
- FCDImage* image = surface->GetImage(0);
- tex = BuildTextureFromImage(image);
- }
- return tex;
-}
-
-// Sets an O3D parameter value from a given FCollada effect parameter.
-// instance.
-// Parameters:
-// param_object: The param_object whose parameter is to be modified.
-// param_name: The name of the parameter to be set.
-// fc_param: The FCollada effect parameter whose value is used.
-// Returns true on success.
-bool Collada::SetParamFromFCEffectParam(ParamObject* param_object,
- const String &param_name,
- FCDEffectParameter *fc_param) {
- if (!param_object || !fc_param) return false;
- FCDEffectParameter::Type type = fc_param->GetType();
- bool rc = false;
- if (type == FCDEffectParameter::FLOAT) {
- FCDEffectParameterFloat* float_param =
- static_cast<FCDEffectParameterFloat*>(fc_param);
- ParamFloat* param = param_object->CreateParam<ParamFloat>(param_name);
- if (param) {
- param->set_value(float_param->GetValue());
- rc = true;
- }
- } else if (type == FCDEffectParameter::FLOAT2) {
- FCDEffectParameterFloat2* float2_param =
- static_cast<FCDEffectParameterFloat2*>(fc_param);
- FMVector2 v = float2_param->GetValue();
- ParamFloat2* param = param_object->CreateParam<ParamFloat2>(param_name);
- if (param) {
- Float2 f(v.x, v.y);
- param->set_value(f);
- rc = true;
- }
- } else if (type == FCDEffectParameter::FLOAT3) {
- FCDEffectParameterFloat3* float3_param =
- static_cast<FCDEffectParameterFloat3*>(fc_param);
- FMVector3 v = float3_param->GetValue();
- ParamFloat3* param = param_object->CreateParam<ParamFloat3>(param_name);
- if (!param)
- return false;
- Float3 f(v.x, v.y, v.z);
- param->set_value(f);
- rc = true;
- } else if (type == FCDEffectParameter::VECTOR) {
- FCDEffectParameterVector* vector_param =
- static_cast<FCDEffectParameterVector*>(fc_param);
- FMVector4 v = vector_param->GetValue();
- ParamFloat4* param = param_object->CreateParam<ParamFloat4>(param_name);
- if (param) {
- Float4 f(v.x, v.y, v.z, v.w);
- param->set_value(f);
- rc = true;
- }
- } else if (type == FCDEffectParameter::INTEGER) {
- FCDEffectParameterInt* int_param =
- static_cast<FCDEffectParameterInt*>(fc_param);
- ParamInteger* param = param_object->CreateParam<ParamInteger>(param_name);
- if (param) {
- param->set_value(int_param->GetValue());
- rc = true;
- }
- } else if (type == FCDEffectParameter::BOOLEAN) {
- FCDEffectParameterBool* bool_param =
- static_cast<FCDEffectParameterBool*>(fc_param);
- ParamBoolean* param = param_object->CreateParam<ParamBoolean>(param_name);
- if (param) {
- param->set_value(bool_param->GetValue());
- rc = true;
- }
- } else if (type == FCDEffectParameter::MATRIX) {
- FCDEffectParameterMatrix* matrix_param =
- static_cast<FCDEffectParameterMatrix*>(fc_param);
- ParamMatrix4* param = param_object->CreateParam<ParamMatrix4>(param_name);
- if (param) {
- param->set_value(FMMatrix44ToMatrix4(matrix_param->GetValue()));
- rc = true;
- }
- } else if (type == FCDEffectParameter::SAMPLER) {
- FCDEffectParameterSampler* sampler =
- static_cast<FCDEffectParameterSampler*>(fc_param);
- ParamSampler* sampler_param = param_object->CreateParam<ParamSampler>(
- param_name);
- if (sampler_param) {
- Sampler* o3d_sampler = pack_->Create<Sampler>();
- o3d_sampler->set_name(param_name);
- sampler_param->set_value(o3d_sampler);
-
- FCDEffectParameterSurface* surface = sampler->GetSurface();
- if (surface) {
- Texture* tex = BuildTexture(surface);
-
- // Set the texture on the sampler.
- if (tex) {
- o3d_sampler->set_texture(tex);
- rc = true;
- }
- }
- SetSamplerStates(sampler, o3d_sampler);
- }
- } else if (type == FCDEffectParameter::SURFACE) {
- // TODO(o3d): This code is here to handle the NV_import profile
- // exported by Max's DirectX Shader materials, which references
- // only references texture params (not samplers). Once we move
- // completely to using samplers and add sampler blocks to our
- // collada file then we should eliminate this codepath.
-
- FCDEffectParameterSurface* surface =
- static_cast<FCDEffectParameterSurface*>(fc_param);
-
- Texture* tex = BuildTexture(surface);
- if (tex) {
- ParamTexture* param = param_object->CreateParam<ParamTexture>(param_name);
- if (param) {
- param->set_value(tex);
- rc = true;
- }
- }
- }
- return rc;
-}
-
-Effect* Collada::GetDummyEffect() {
- if (!dummy_effect_) {
- // Create a dummy effect, just so we can see something
- dummy_effect_ = pack_->Create<Effect>();
- dummy_effect_->set_name(
- COLLADA_STRING_CONSTANT("substituteForMissingOrBadEffect"));
- dummy_effect_->LoadFromFXString(
- "float4x4 worldViewProj : WorldViewProjection;"
- "float4 vs(float4 v : POSITION ) : POSITION {"
- " return mul(v, worldViewProj);"
- "}"
- "float4 ps() : COLOR {"
- " return float4(1, 0, 1, 1);"
- "}\n"
- "// #o3d VertexShaderEntryPoint vs\n"
- "// #o3d PixelShaderEntryPoint ps\n");
- }
- return dummy_effect_;
-}
-
-Material* Collada::GetDummyMaterial() {
- if (!dummy_material_) {
- dummy_material_ = pack_->Create<Material>();
- dummy_material_->set_name(
- COLLADA_STRING_CONSTANT("substituteForMissingOrBadMaterial"));
- dummy_material_->set_effect(GetDummyEffect());
- }
- return dummy_material_;
-}
-
-static const char* GetLightingType(FCDEffectStandard* std_profile) {
- FCDEffectStandard::LightingType type = std_profile->GetLightingType();
- switch (type) {
- case FCDEffectStandard::CONSTANT:
- return Collada::kLightingTypeConstant;
- case FCDEffectStandard::PHONG:
- return Collada::kLightingTypePhong;
- case FCDEffectStandard::BLINN:
- return Collada::kLightingTypeBlinn;
- case FCDEffectStandard::LAMBERT:
- return Collada::kLightingTypeLambert;
- default:
- return Collada::kLightingTypeUnknown;
- }
-}
-
-static FCDEffectProfileFX* FindProfileFX(FCDEffect* effect) {
- FCDEffectProfile* profile = effect->FindProfile(FUDaeProfileType::HLSL);
- if (!profile) {
- profile = effect->FindProfile(FUDaeProfileType::CG);
- if (!profile) {
- return NULL;
- }
- }
- return static_cast<FCDEffectProfileFX*>(profile);
-}
-
-Material* Collada::BuildMaterial(FCDocument* doc,
- FCDMaterial* collada_material) {
- if (!doc || !collada_material) {
- return NULL;
- }
-
- fm::string material_id = collada_material->GetDaeId();
- Material* material = materials_[material_id.c_str()];
- if (!material) {
- Effect* effect = NULL;
- FCDEffect* collada_effect = collada_material->GetEffect();
- if (collada_effect) {
- effect = GetEffect(doc, collada_effect);
- }
-
- String collada_material_name = WideToUTF8(
- collada_material->GetName().c_str());
- Material* material = pack_->Create<Material>();
- material->set_name(collada_material_name);
- material->set_effect(effect);
- SetParamsFromMaterial(collada_material, material);
-
- // If this is a COLLADA-FX profile, add the render states from the
- // COLLADA-FX sections.
- FCDEffectProfileFX* profile_fx = FindProfileFX(collada_effect);
- if (profile_fx) {
- if (profile_fx->GetTechniqueCount() > 0) {
- FCDEffectTechnique* technique = profile_fx->GetTechnique(0);
- if (technique->GetPassCount() > 0) {
- FCDEffectPass* pass = technique->GetPass(0);
- State* state = pack_->Create<State>();
- state->set_name("pass_state");
- cull_enabled_ = cull_front_ = front_cw_ = false;
- for (size_t i = 0; i < pass->GetRenderStateCount(); ++i) {
- AddRenderState(pass->GetRenderState(i), state);
- }
- material->set_state(state);
- }
- }
- } else {
- FCDEffectStandard* std_profile = static_cast<FCDEffectStandard *>(
- collada_effect->FindProfile(FUDaeProfileType::COMMON));
- if (std_profile) {
- ParamString* type_tag = material->CreateParam<ParamString>(
- kLightingTypeParamName);
- type_tag->set_value(GetLightingType(std_profile));
- }
- }
- materials_[material_id.c_str()] = material;
- }
- return material;
-}
-
-Effect* Collada::GetEffect(FCDocument* doc, FCDEffect* collada_effect) {
- Effect* effect = NULL;
- fm::string effect_id = collada_effect->GetDaeId();
- effect = effects_[effect_id.c_str()];
- if (!effect) {
- effect = BuildEffect(doc, collada_effect);
- if (effect) effects_[effect_id.c_str()] = effect;
- }
- return effect;
-}
-
-// Builds an O3D effect from a COLLADA effect. If a COLLADA-FX
-// (Cg/HLSL) effect is present, it will be used and a programmable
-// Effect generated. If not, an attempt is made to use one of the fixed-
-// function profiles if present (eg., Constant, Lambert).
-// Parameters:
-// doc: The FCollada document from which to import nodes.
-// collada_effect: The COLLADA effect from which shaders will be taken.
-// Returns:
-// the newly-created effect, or NULL or error.
-Effect* Collada::BuildEffect(FCDocument* doc, FCDEffect* collada_effect) {
- if (!doc || !collada_effect) return NULL;
- // TODO(o3d): Remove all of this to be replaced by parsing Collada-FX
- // and profile_O3D only.
- Effect* effect = NULL;
- FCDEffectProfileFX* profile_fx = FindProfileFX(collada_effect);
- if (profile_fx) {
- if (profile_fx->GetCodeCount() > 0) {
- FCDEffectCode* code = profile_fx->GetCode(0);
- String effect_string;
- FilePath file_path;
- if (code->GetType() == FCDEffectCode::CODE) {
- fstring code_string = code->GetCode();
- effect_string = WideToUTF8(code_string.c_str());
- unique_filename_counter_++;
- String file_name = "embedded-shader-" +
- IntToString(unique_filename_counter_) + ".fx";
- file_path = FilePath(FILE_PATH_LITERAL("shaders"));
- file_path = file_path.Append(UTF8ToFilePath(file_name));
- } else if (code->GetType() == FCDEffectCode::INCLUDE) {
- fstring path = code->GetFilename();
- file_path = WideToFilePath(path.c_str());
- if (collada_zip_archive_ && !file_path.empty()) {
- // Make absolute path be relative to root of archive.
- if (file_path.value()[0] == FILE_PATH_LITERAL('/')) {
- file_path = FilePath(file_path.value().substr(1));
- }
- size_t effect_data_size;
- char *effect_data =
- collada_zip_archive_->GetFileData(FilePathToUTF8(file_path),
- &effect_data_size);
- if (effect_data) {
- effect_string = effect_data;
- free(effect_data);
- } else {
- O3D_ERROR(service_locator_)
- << "Unable to read effect data for effect '"
- << FilePathToUTF8(file_path) << "'";
- return NULL;
- }
- } else {
- FilePath temp_path = file_path;
- if (!FindFile(options_.file_paths, temp_path, &temp_path)) {
- O3D_ERROR(service_locator_) << "Could not find file: " << path;
- return NULL;
- }
- file_util::ReadFileToString(temp_path, &effect_string);
- }
- }
- String collada_effect_name = WideToUTF8(
- collada_effect->GetName().c_str());
- effect = pack_->Create<Effect>();
- effect->set_name(collada_effect_name);
-
- ParamString* param = effect->CreateParam<ParamString>(
- O3D_STRING_CONSTANT("uri"));
- DCHECK(param != NULL);
- param->set_value(FilePathToUTF8(file_path));
-
- if (!effect->LoadFromFXString(effect_string)) {
- pack_->RemoveObject(effect);
- O3D_ERROR(service_locator_) << "Unable to load effect '"
- << FilePathToUTF8(file_path).c_str()
- << "'";
- return NULL;
- }
-
- if (options_.convert_cg_to_glsl) {
- if (!ConvertCgToGlsl(options_.converter_tool, &effect_string))
- O3D_ERROR(service_locator_) << "Shader conversion failed.";
- }
-
- if (options_.keep_original_data) {
- // Cache the original data by URI so we can recover it later.
- original_data_map_.AddData(file_path, effect_string, service_locator_);
- }
- }
- } else {
- FCDExtra* extra = collada_effect->GetExtra();
- if (extra && extra->GetTypeCount() > 0) {
- FCDEType* type = extra->GetType(0);
- if (type) {
- FCDETechnique* technique = type->FindTechnique("NV_import");
- if (technique) {
- FCDENode* node = technique->FindChildNode("import");
- if (node) {
- FCDEAttribute* url_attrib = node->FindAttribute("url");
- if (url_attrib) {
- fstring url = url_attrib->GetValue();
- const FUFileManager* mgr = doc->GetFileManager();
- LOG_ASSERT(mgr != NULL);
- FUUri uri = mgr->GetCurrentUri();
- FUUri effect_uri = uri.Resolve(url_attrib->GetValue());
- fstring path = effect_uri.GetAbsolutePath();
-
- String collada_effect_name = WideToUTF8(
- collada_effect->GetName().c_str());
-
- FilePath file_path = WideToFilePath(path.c_str());
-
- String effect_string;
-
- if (collada_zip_archive_ && !file_path.empty()) {
- // Make absolute path be relative to root of archive.
- if (file_path.value()[0] == FILE_PATH_LITERAL('/')) {
- file_path = FilePath(file_path.value().substr(1));
- }
- // shader file can be extracted in memory from zip archive
- // so lets get the data and turn it into a string
- size_t effect_data_size;
- char *effect_data =
- collada_zip_archive_->GetFileData(FilePathToUTF8(file_path),
- &effect_data_size);
- if (effect_data) {
- effect_string = effect_data;
- free(effect_data);
- } else {
- O3D_ERROR(service_locator_)
- << "Unable to read effect data for effect '"
- << FilePathToUTF8(file_path) << "'";
- return NULL;
- }
- } else {
- file_util::ReadFileToString(file_path, &effect_string);
- }
-
- effect = pack_->Create<Effect>();
- effect->set_name(collada_effect_name);
-
- ParamString* param = effect->CreateParam<ParamString>(
- O3D_STRING_CONSTANT("uri"));
- DCHECK(param != NULL);
- param->set_value(FilePathToUTF8(file_path));
-
- if (!effect->LoadFromFXString(effect_string)) {
- pack_->RemoveObject(effect);
- O3D_ERROR(service_locator_) << "Unable to load effect '"
- << FilePathToUTF8(file_path).c_str()
- << "'";
- return NULL;
- }
- if (options_.keep_original_data) {
- // Cache the original data by URI so we can recover it later.
- original_data_map_.AddData(file_path, effect_string,
- service_locator_);
- }
- }
- }
- }
- }
- }
- }
- return effect;
-}
-
-// Gets a typed value from an FCollada state. T is the type to get,
-// state is the state from which to retrieve the value, and offset is
-// the index into the state's data (in sizeof T objects) at which the
-// value is located.
-template <class T>
-static T GetStateValue(FCDEffectPassState* state, size_t offset) {
- LOG_ASSERT(offset * sizeof(T) < state->GetDataSize());
- return *(reinterpret_cast<T*>(state->GetData()) + offset);
-}
-
-// Converts an FCollada blend type into an O3D state blending function.
-// On error, an error string is logged. "type" is the FCollada type to convert,
-// and "collada" is the Collada importer used for logging errors.
-static State::BlendingFunction ConvertBlendType(
- ServiceLocator* service_locator,
- FUDaePassStateBlendType::Type type) {
- switch (type) {
- case FUDaePassStateBlendType::ZERO:
- return State::BLENDFUNC_ZERO;
- case FUDaePassStateBlendType::ONE:
- return State::BLENDFUNC_ONE;
- case FUDaePassStateBlendType::SOURCE_COLOR:
- return State::BLENDFUNC_SOURCE_COLOR;
- case FUDaePassStateBlendType::ONE_MINUS_SOURCE_COLOR:
- return State::BLENDFUNC_INVERSE_SOURCE_COLOR;
- case FUDaePassStateBlendType::SOURCE_ALPHA:
- return State::BLENDFUNC_SOURCE_ALPHA;
- case FUDaePassStateBlendType::ONE_MINUS_SOURCE_ALPHA:
- return State::BLENDFUNC_INVERSE_SOURCE_ALPHA;
- case FUDaePassStateBlendType::DESTINATION_ALPHA:
- return State::BLENDFUNC_DESTINATION_ALPHA;
- case FUDaePassStateBlendType::ONE_MINUS_DESTINATION_ALPHA:
- return State::BLENDFUNC_INVERSE_DESTINATION_ALPHA;
- case FUDaePassStateBlendType::DESTINATION_COLOR:
- return State::BLENDFUNC_DESTINATION_COLOR;
- case FUDaePassStateBlendType::ONE_MINUS_DESTINATION_COLOR:
- return State::BLENDFUNC_INVERSE_DESTINATION_COLOR;
- case FUDaePassStateBlendType::SOURCE_ALPHA_SATURATE:
- return State::BLENDFUNC_SOURCE_ALPHA_SATUTRATE;
- default:
- O3D_ERROR(service_locator) << "Invalid blend type";
- return State::BLENDFUNC_ONE;
- }
-}
-
-// Converts an FCollada blend equation into an O3D state blending
-// equation. On error, an error string is logged. "equation" is the
-// FCollada equation to convert, and "collada" is the Collada importer
-// used for logging errors.
-static State::BlendingEquation ConvertBlendEquation(
- ServiceLocator* service_locator,
- FUDaePassStateBlendEquation::Equation equation) {
- switch (equation) {
- case FUDaePassStateBlendEquation::ADD:
- return State::BLEND_ADD;
- case FUDaePassStateBlendEquation::SUBTRACT:
- return State::BLEND_SUBTRACT;
- case FUDaePassStateBlendEquation::REVERSE_SUBTRACT:
- return State::BLEND_REVERSE_SUBTRACT;
- case FUDaePassStateBlendEquation::MIN:
- return State::BLEND_MIN;
- case FUDaePassStateBlendEquation::MAX:
- return State::BLEND_MAX;
- default:
- O3D_ERROR(service_locator) << "Invalid blend equation";
- return State::BLEND_ADD;
- }
-}
-
-// Converts an FCollada comparison function into an O3D state blending
-// equation. On error, an error string is logged. "function" is the
-// FCollada comparison function to convert, and "collada" is the Collada
-// importer used for logging errors.
-static State::Comparison ConvertComparisonFunction(
- ServiceLocator* service_locator,
- FUDaePassStateFunction::Function function) {
- switch (function) {
- case FUDaePassStateFunction::NEVER:
- return State::CMP_NEVER;
- case FUDaePassStateFunction::LESS:
- return State::CMP_LESS;
- case FUDaePassStateFunction::LESS_EQUAL:
- return State::CMP_LEQUAL;
- case FUDaePassStateFunction::EQUAL:
- return State::CMP_EQUAL;
- case FUDaePassStateFunction::GREATER:
- return State::CMP_GREATER;
- case FUDaePassStateFunction::NOT_EQUAL:
- return State::CMP_NOTEQUAL;
- case FUDaePassStateFunction::GREATER_EQUAL:
- return State::CMP_GEQUAL;
- case FUDaePassStateFunction::ALWAYS:
- return State::CMP_ALWAYS;
- default:
- O3D_ERROR(service_locator) << "Invalid comparison function";
- return State::CMP_NEVER;
- }
-}
-
-// Converts an FCollada polygon fill mode into an O3D fill mode.
-// On error, an error string is logged. "mode" is the FCollada fill
-// mode to convert, and "collada" is the Collada importer used for
-// logging errors.
-static State::Fill ConvertFillMode(ServiceLocator* service_locator,
- FUDaePassStatePolygonMode::Mode mode) {
- switch (mode) {
- case FUDaePassStatePolygonMode::POINT:
- return State::POINT;
- case FUDaePassStatePolygonMode::LINE:
- return State::WIREFRAME;
- case FUDaePassStatePolygonMode::FILL:
- return State::SOLID;
- default:
- O3D_ERROR(service_locator) << "Invalid polygon fill mode";
- return State::SOLID;
- }
-}
-
-// Converts an FCollada stencil operation into an O3D stencil operation.
-// On error, an error string is logged. "operation" is the FCollada operation
-// to convert, and "collada" is the Collada importer used for logging errors.
-static State::StencilOperation ConvertStencilOp(
- ServiceLocator* service_locator,
- FUDaePassStateStencilOperation::Operation operation) {
- switch (operation) {
- case FUDaePassStateStencilOperation::KEEP:
- return State::STENCIL_KEEP;
- case FUDaePassStateStencilOperation::ZERO:
- return State::STENCIL_ZERO;
- case FUDaePassStateStencilOperation::REPLACE:
- return State::STENCIL_REPLACE;
- case FUDaePassStateStencilOperation::INCREMENT:
- return State::STENCIL_INCREMENT_SATURATE;
- case FUDaePassStateStencilOperation::DECREMENT:
- return State::STENCIL_DECREMENT_SATURATE;
- case FUDaePassStateStencilOperation::INVERT:
- return State::STENCIL_INVERT;
- case FUDaePassStateStencilOperation::INCREMENT_WRAP:
- return State::STENCIL_INCREMENT;
- case FUDaePassStateStencilOperation::DECREMENT_WRAP:
- return State::STENCIL_DECREMENT;
- default:
- O3D_ERROR(service_locator) << "Invalid stencil operation";
- return State::STENCIL_KEEP;
- }
-}
-
-// Updates the O3D state object's cull mode from the OpenGL-style
-// cull modes used by COLLADA-FX sections. "state" is the O3D state
-// on which to set the cull mode. If "cull_front_" is true, the system is
-// culling front-facing polygons, otherwise it's culling back-facing. If
-// "front_cw_" is true, polygons with clockwise winding are considered
-// front-facing, otherwise counter-clockwise winding is considered
-// front-facing. if "cull_enabled_" is false, culling is disabled.
-void Collada::UpdateCullingState(State* state) {
- ParamInteger* face = state->GetStateParam<ParamInteger>(
- State::kCullModeParamName);
- if (cull_front_ ^ front_cw_) {
- face->set_value(cull_enabled_ ? State::CULL_CCW : State::CULL_NONE);
- } else {
- face->set_value(cull_enabled_ ? State::CULL_CW : State::CULL_NONE);
- }
-}
-
-// Sets a boolean state param in an O3D state object. "state" is the
-// state object, "name" is the name of the state param to set, and "value"
-// is the boolean value. If the state param does not exist, or is not
-// of the correct type, an assert is logged.
-static void SetBoolState(State* state, const char* name, bool value) {
- ParamBoolean* param = state->GetStateParam<ParamBoolean>(name);
- LOG_ASSERT(param);
- param->set_value(value);
-}
-
-// Sets a floating-point state param in an O3D state object.
-// "state" is the state object, "name" is the name of the state param
-// to set, and "value" is the floating-point value. If the state
-// param does not exist, or is not of the correct type, an assert
-// is logged.
-static void SetFloatState(State* state, const char* name, float value) {
- ParamFloat* param = state->GetStateParam<ParamFloat>(name);
- LOG_ASSERT(param);
- param->set_value(value);
-}
-
-// Sets a Float4 state param in an O3D state object. "state" is
-// the state object, "name" is the name of the state param to set, and
-// "value" is the Float4 value. If the state param does not exist, or
-// is not of the correct type, an assert is logged.
-static void SetFloat4State(State* state,
- const char* name,
- const Float4& value) {
- ParamFloat4* param = state->GetStateParam<ParamFloat4>(name);
- LOG_ASSERT(param);
- param->set_value(value);
-}
-
-// Sets a integer state param in an O3D state object. "state" is
-// the state object, "name" is the name of the state param to set, and
-// "value" is the Float4 value. If the state param does not exist, or
-// is not of the correct type, an assert is logged.
-static void SetIntState(State* state, const char* name, int value) {
- ParamInteger* param = state->GetStateParam<ParamInteger>(name);
- LOG_ASSERT(param);
- param->set_value(value);
-}
-
-// Sets the stencil state params for polygons with clockwise
-// winding. "state" is the O3D state object in which to set
-// state, "fail", "zfail", and "zpass" are the corresponding stencil
-// settings.
-static void SetCWStencilSettings(State* state,
- State::StencilOperation fail,
- State::StencilOperation zfail,
- State::StencilOperation zpass) {
- SetIntState(state, State::kStencilFailOperationParamName, fail);
- SetIntState(state, State::kStencilZFailOperationParamName, zfail);
- SetIntState(state, State::kStencilPassOperationParamName, zpass);
-}
-
-// Sets the stencil state params for polygons with counter-clockwise
-// winding. "state" is the O3D state object in which to set
-// state, "fail", "zfail", and "zpass" are the corresponding stencil
-// settings.
-static void SetCCWStencilSettings(State* state,
- State::StencilOperation fail,
- State::StencilOperation zfail,
- State::StencilOperation zpass) {
- SetIntState(state, State::kCCWStencilFailOperationParamName, fail);
- SetIntState(state, State::kCCWStencilZFailOperationParamName, zfail);
- SetIntState(state, State::kCCWStencilPassOperationParamName, zpass);
-}
-
-// Sets the stencil state params for polygons with the given winding
-// order. "state" is the O3D state object in which to set
-// state, "fail", "zfail", and "zpass" are the corresponding stencil
-// settings. If "cw" is true, clockwise winding settings will be set,
-// otherwise counter-clockwise winding settings are set.
-static void SetStencilSettings(State* state,
- bool cw,
- State::StencilOperation fail,
- State::StencilOperation zfail,
- State::StencilOperation zpass) {
- if (cw) {
- SetCWStencilSettings(state, fail, zfail, zpass);
- } else {
- SetCCWStencilSettings(state, fail, zfail, zpass);
- }
-}
-
-// Adds the appropriate O3D state params to the given state object
-// corresponding to a given FCollada Pass State. If unsupported or
-// invalid states are specified, an error message is set.
-void Collada::AddRenderState(FCDEffectPassState* pass_state, State* state) {
- switch (pass_state->GetType()) {
- case FUDaePassState::ALPHA_FUNC: {
- State::Comparison function = ConvertComparisonFunction(
- service_locator_,
- GetStateValue<FUDaePassStateFunction::Function>(pass_state, 0));
- float value = GetStateValue<float>(pass_state, 1);
- SetIntState(state, State::kAlphaComparisonFunctionParamName, function);
- SetFloatState(state, State::kAlphaReferenceParamName, value);
- break;
- }
- case FUDaePassState::BLEND_FUNC: {
- State::BlendingFunction src = ConvertBlendType(
- service_locator_,
- GetStateValue<FUDaePassStateBlendType::Type>(pass_state, 0));
- State::BlendingFunction dest = ConvertBlendType(
- service_locator_,
- GetStateValue<FUDaePassStateBlendType::Type>(pass_state, 1));
- SetIntState(state, State::kSourceBlendFunctionParamName, src);
- SetIntState(state, State::kDestinationBlendFunctionParamName, dest);
- SetBoolState(state, State::kSeparateAlphaBlendEnableParamName, false);
- break;
- }
- case FUDaePassState::BLEND_FUNC_SEPARATE: {
- State::BlendingFunction src_rgb = ConvertBlendType(
- service_locator_,
- GetStateValue<FUDaePassStateBlendType::Type>(pass_state, 0));
- State::BlendingFunction dest_rgb = ConvertBlendType(
- service_locator_,
- GetStateValue<FUDaePassStateBlendType::Type>(pass_state, 1));
- State::BlendingFunction src_alpha = ConvertBlendType(
- service_locator_,
- GetStateValue<FUDaePassStateBlendType::Type>(pass_state, 2));
- State::BlendingFunction dest_alpha = ConvertBlendType(
- service_locator_,
- GetStateValue<FUDaePassStateBlendType::Type>(pass_state, 3));
- SetIntState(state, State::kSourceBlendFunctionParamName, src_rgb);
- SetIntState(state, State::kDestinationBlendFunctionParamName, dest_rgb);
- SetIntState(state, State::kSourceBlendAlphaFunctionParamName, src_alpha);
- SetIntState(state, State::kDestinationBlendAlphaFunctionParamName,
- dest_alpha);
- SetBoolState(state, State::kSeparateAlphaBlendEnableParamName, true);
- break;
- }
- case FUDaePassState::BLEND_EQUATION: {
- State::BlendingEquation value = ConvertBlendEquation(
- service_locator_,
- GetStateValue<FUDaePassStateBlendEquation::Equation>(pass_state, 0));
- SetIntState(state, State::kBlendEquationParamName, value);
- SetIntState(state, State::kBlendAlphaEquationParamName, value);
- break;
- }
- case FUDaePassState::BLEND_EQUATION_SEPARATE: {
- State::BlendingEquation rgb = ConvertBlendEquation(
- service_locator_,
- GetStateValue<FUDaePassStateBlendEquation::Equation>(pass_state, 0));
- State::BlendingEquation alpha = ConvertBlendEquation(
- service_locator_,
- GetStateValue<FUDaePassStateBlendEquation::Equation>(pass_state, 1));
- SetIntState(state, State::kBlendEquationParamName, rgb);
- SetIntState(state, State::kBlendAlphaEquationParamName, alpha);
- break;
- }
- case FUDaePassState::CULL_FACE: {
- FUDaePassStateFaceType::Type culled_faces =
- GetStateValue<FUDaePassStateFaceType::Type>(pass_state, 0);
- switch (culled_faces) {
- case FUDaePassStateFaceType::FRONT: {
- cull_front_ = true;
- break;
- }
- case FUDaePassStateFaceType::BACK: {
- cull_front_ = false;
- break;
- }
- case FUDaePassStateFaceType::FRONT_AND_BACK: {
- O3D_ERROR(service_locator_)
- << "FRONT_AND_BACK culling is unsupported";
- break;
- }
- case FUDaePassStateFaceType::INVALID:
- O3D_ERROR(service_locator_) << "INVALID culling type";
- break;
- }
- UpdateCullingState(state);
- break;
- }
- case FUDaePassState::DEPTH_FUNC: {
- State::Comparison function = ConvertComparisonFunction(
- service_locator_,
- GetStateValue<FUDaePassStateFunction::Function>(pass_state, 0));
- SetIntState(state, State::kZComparisonFunctionParamName, function);
- break;
- }
- case FUDaePassState::FRONT_FACE: {
- FUDaePassStateFrontFaceType::Type type =
- GetStateValue<FUDaePassStateFrontFaceType::Type>(pass_state, 0);
- front_cw_ = type == FUDaePassStateFrontFaceType::CLOCKWISE;
- break;
- }
- case FUDaePassState::POLYGON_MODE: {
- FUDaePassStateFaceType::Type face =
- GetStateValue<FUDaePassStateFaceType::Type>(pass_state, 0);
- State::Fill mode = ConvertFillMode(
- service_locator_,
- GetStateValue<FUDaePassStatePolygonMode::Mode>(pass_state, 1));
- if (face != FUDaePassStateFaceType::FRONT_AND_BACK) {
- O3D_ERROR(service_locator_)
- << "Separate polygon fill modes are unsupported";
- }
- SetIntState(state, State::kFillModeParamName, mode);
- break;
- }
- case FUDaePassState::STENCIL_FUNC: {
- State::Comparison func = ConvertComparisonFunction(
- service_locator_,
- GetStateValue<FUDaePassStateFunction::Function>(pass_state, 0));
- uint8 ref = GetStateValue<uint8>(pass_state, 4);
- uint8 mask = GetStateValue<uint8>(pass_state, 5);
- SetIntState(state, State::kStencilComparisonFunctionParamName, func);
- SetIntState(state, State::kStencilReferenceParamName, ref);
- SetIntState(state, State::kStencilMaskParamName, mask);
- SetBoolState(state, State::kTwoSidedStencilEnableParamName, false);
- break;
- }
- case FUDaePassState::STENCIL_FUNC_SEPARATE: {
- State::Comparison front = ConvertComparisonFunction(
- service_locator_,
- GetStateValue<FUDaePassStateFunction::Function>(pass_state, 0));
- State::Comparison back = ConvertComparisonFunction(
- service_locator_,
- GetStateValue<FUDaePassStateFunction::Function>(pass_state, 1));
- uint8 ref = GetStateValue<uint8>(pass_state, 8);
- uint8 mask = GetStateValue<uint8>(pass_state, 9);
- SetIntState(state, State::kStencilComparisonFunctionParamName, front);
- SetIntState(state, State::kCCWStencilComparisonFunctionParamName, back);
- SetIntState(state, State::kStencilReferenceParamName, ref);
- SetIntState(state, State::kStencilMaskParamName, mask);
- SetBoolState(state, State::kTwoSidedStencilEnableParamName, true);
- break;
- }
- case FUDaePassState::STENCIL_OP: {
- State::StencilOperation fail = ConvertStencilOp(
- service_locator_,
- GetStateValue<FUDaePassStateStencilOperation::Operation>(pass_state,
- 0));
- State::StencilOperation zfail = ConvertStencilOp(
- service_locator_,
- GetStateValue<FUDaePassStateStencilOperation::Operation>(pass_state,
- 1));
- State::StencilOperation zpass = ConvertStencilOp(
- service_locator_,
- GetStateValue<FUDaePassStateStencilOperation::Operation>(pass_state,
- 2));
- SetStencilSettings(state, front_cw_, fail, zfail, zpass);
- SetBoolState(state, State::kTwoSidedStencilEnableParamName, false);
- break;
- }
- case FUDaePassState::STENCIL_OP_SEPARATE: {
- FUDaePassStateFaceType::Type type =
- GetStateValue<FUDaePassStateFaceType::Type>(pass_state, 0);
- State::StencilOperation fail = ConvertStencilOp(
- service_locator_,
- GetStateValue<FUDaePassStateStencilOperation::Operation>(pass_state,
- 0));
- State::StencilOperation zfail = ConvertStencilOp(
- service_locator_,
- GetStateValue<FUDaePassStateStencilOperation::Operation>(pass_state,
- 1));
- State::StencilOperation zpass = ConvertStencilOp(
- service_locator_,
- GetStateValue<FUDaePassStateStencilOperation::Operation>(pass_state,
- 2));
- switch (type) {
- case FUDaePassStateFaceType::FRONT:
- SetStencilSettings(state, front_cw_, fail, zfail, zpass);
- SetBoolState(state, State::kTwoSidedStencilEnableParamName, true);
- break;
- case FUDaePassStateFaceType::BACK:
- SetStencilSettings(state, !front_cw_, fail, zfail, zpass);
- SetBoolState(state, State::kTwoSidedStencilEnableParamName, true);
- break;
- case FUDaePassStateFaceType::FRONT_AND_BACK:
- SetStencilSettings(state, front_cw_, fail, zfail, zpass);
- SetStencilSettings(state, !front_cw_, fail, zfail, zpass);
- SetBoolState(state, State::kTwoSidedStencilEnableParamName, false);
- break;
- default:
- O3D_ERROR(service_locator_)
- << "Unknown polygon face mode in STENCIL_OP_SEPARATE";
- break;
- }
- break;
- }
- case FUDaePassState::STENCIL_MASK: {
- uint32 mask = GetStateValue<uint8>(pass_state, 0);
- SetIntState(state, State::kStencilWriteMaskParamName, mask);
- break;
- }
- case FUDaePassState::STENCIL_MASK_SEPARATE: {
- O3D_ERROR(service_locator_) << "Separate stencil mask is unsupported";
- break;
- }
- case FUDaePassState::COLOR_MASK: {
- bool red = GetStateValue<bool>(pass_state, 0);
- bool green = GetStateValue<bool>(pass_state, 1);
- bool blue = GetStateValue<bool>(pass_state, 2);
- bool alpha = GetStateValue<bool>(pass_state, 3);
- int mask = 0x0;
- if (red) mask |= 0x1;
- if (green) mask |= 0x2;
- if (blue) mask |= 0x4;
- if (alpha) mask |= 0x8;
- SetIntState(state, State::kColorWriteEnableParamName, mask);
- break;
- }
- case FUDaePassState::DEPTH_MASK: {
- bool value = GetStateValue<bool>(pass_state, 0);
- SetBoolState(state, State::kZWriteEnableParamName, value);
- break;
- }
- case FUDaePassState::POINT_SIZE: {
- float value = GetStateValue<float>(pass_state, 0);
- SetFloatState(state, State::kPointSizeParamName, value);
- break;
- }
- case FUDaePassState::POLYGON_OFFSET: {
- float value1 = GetStateValue<float>(pass_state, 0);
- float value2 = GetStateValue<float>(pass_state, 1);
- SetFloatState(state, State::kPolygonOffset1ParamName, value1);
- SetFloatState(state, State::kPolygonOffset2ParamName, value2);
- break;
- }
- case FUDaePassState::BLEND_COLOR: {
- FMVector4 value = GetStateValue<FMVector4>(pass_state, 0);
- Float4 v(value.x, value.y, value.z, value.w);
- SetFloat4State(state, State::kPolygonOffset1ParamName, v);
- break;
- }
- case FUDaePassState::ALPHA_TEST_ENABLE: {
- bool value = GetStateValue<bool>(pass_state, 0);
- SetBoolState(state, State::kAlphaTestEnableParamName, value);
- break;
- }
- case FUDaePassState::BLEND_ENABLE: {
- bool value = GetStateValue<bool>(pass_state, 0);
- SetBoolState(state, State::kAlphaBlendEnableParamName, value);
- break;
- }
- case FUDaePassState::CULL_FACE_ENABLE: {
- cull_enabled_ = GetStateValue<bool>(pass_state, 0);
- UpdateCullingState(state);
- break;
- }
- case FUDaePassState::DEPTH_TEST_ENABLE: {
- bool value = GetStateValue<bool>(pass_state, 0);
- SetBoolState(state, State::kZEnableParamName, value);
- break;
- }
- case FUDaePassState::DITHER_ENABLE: {
- bool value = GetStateValue<bool>(pass_state, 0);
- SetBoolState(state, State::kDitherEnableParamName, value);
- break;
- }
- case FUDaePassState::LINE_SMOOTH_ENABLE: {
- bool value = GetStateValue<bool>(pass_state, 0);
- SetBoolState(state, State::kLineSmoothEnableParamName, value);
- break;
- }
- case FUDaePassState::STENCIL_TEST_ENABLE: {
- bool value = GetStateValue<bool>(pass_state, 0);
- SetBoolState(state, State::kStencilEnableParamName, value);
- break;
- }
- default:
- // do nothing
- break;
- }
-}
-
-// Converts an FCollada texture sampler wrap mode to an O3D Sampler
-// AddressMode.
-static Sampler::AddressMode ConvertSamplerAddressMode(
- FUDaeTextureWrapMode::WrapMode wrap_mode) {
- switch (wrap_mode) {
- case FUDaeTextureWrapMode::WRAP:
- return Sampler::WRAP;
- case FUDaeTextureWrapMode::MIRROR:
- return Sampler::MIRROR;
- case FUDaeTextureWrapMode::CLAMP:
- return Sampler::CLAMP;
- case FUDaeTextureWrapMode::BORDER:
- return Sampler::BORDER;
- default:
- return Sampler::WRAP;
- }
-}
-
-// Converts an FCollada filter func to an O3D Sampler FilterType.
-// Since the Collada filter spec allows both GL-style combo mag/min filters,
-// and DX-style (separate min/mag/mip filters), this function extracts
-// only the first (min) part of a GL-style filter.
-static Sampler::FilterType ConvertFilterType(
- FUDaeTextureFilterFunction::FilterFunction filter_function,
- bool allow_none) {
- switch (filter_function) {
- case FUDaeTextureFilterFunction::NEAREST:
- case FUDaeTextureFilterFunction::NEAREST_MIPMAP_NEAREST:
- case FUDaeTextureFilterFunction::NEAREST_MIPMAP_LINEAR:
- return Sampler::POINT;
- case FUDaeTextureFilterFunction::LINEAR:
- case FUDaeTextureFilterFunction::LINEAR_MIPMAP_NEAREST:
- case FUDaeTextureFilterFunction::LINEAR_MIPMAP_LINEAR:
- return Sampler::LINEAR;
- // TODO(o3d): Once FCollada supports COLLADA v1.5, turn this on:
- // case FUDaeTextureFilterFunction::ANISOTROPIC:
- // return Sampler::ANISOTROPIC;
- case FUDaeTextureFilterFunction::NONE:
- return allow_none ? Sampler::NONE : Sampler::LINEAR;
- default:
- return Sampler::LINEAR;
- }
-}
-
-
-// Retrieves the mipmap part of a GL-style filter function.
-// If no mipmap part is specified, it is assumed to be POINT.
-static Sampler::FilterType ConvertMipmapFilter(
- FUDaeTextureFilterFunction::FilterFunction filter_function) {
- switch (filter_function) {
- case FUDaeTextureFilterFunction::NEAREST_MIPMAP_NEAREST:
- case FUDaeTextureFilterFunction::LINEAR_MIPMAP_NEAREST:
- case FUDaeTextureFilterFunction::UNKNOWN:
- return Sampler::POINT;
- case FUDaeTextureFilterFunction::NEAREST_MIPMAP_LINEAR:
- case FUDaeTextureFilterFunction::LINEAR_MIPMAP_LINEAR:
- return Sampler::LINEAR;
- default:
- return Sampler::NONE;
- }
-}
-
-// Sets the texture sampler states on an O3D sampler from the
-// settings found in an FCollada sampler.
-// Parameters:
-// effect_sampler: The FCollada sampler.
-// o3d_sampler: The O3D sampler object.
-void Collada::SetSamplerStates(FCDEffectParameterSampler* effect_sampler,
- Sampler* o3d_sampler) {
- FUDaeTextureWrapMode::WrapMode wrap_s = effect_sampler->GetWrapS();
- FUDaeTextureWrapMode::WrapMode wrap_t = effect_sampler->GetWrapT();
- Texture *texture = o3d_sampler->texture();
- if (texture && texture->IsA(TextureCUBE::GetApparentClass())) {
- // Our default is WRAP, but cube maps should use CLAMP
- if (wrap_s == FUDaeTextureWrapMode::UNKNOWN)
- wrap_s = FUDaeTextureWrapMode::CLAMP;
- if (wrap_t == FUDaeTextureWrapMode::UNKNOWN)
- wrap_t = FUDaeTextureWrapMode::CLAMP;
- }
-
- FUDaeTextureFilterFunction::FilterFunction min_filter =
- effect_sampler->GetMinFilter();
- FUDaeTextureFilterFunction::FilterFunction mag_filter =
- effect_sampler->GetMagFilter();
-
- FUDaeTextureFilterFunction::FilterFunction mip_filter =
- effect_sampler->GetMipFilter();
- // TODO(o3d): Once FCollada supports COLLADA v1.5, turn this on:
- // int max_anisotropy = effect_sampler->GetMaxAnisotropy();
-
- o3d_sampler->set_address_mode_u(ConvertSamplerAddressMode(wrap_s));
- o3d_sampler->set_address_mode_v(ConvertSamplerAddressMode(wrap_t));
-
- // The Collada spec allows for both DX-style and GL-style specification
- // of texture filtering modes. In DX-style, Min, Mag and Mip filters
- // are specified separately, and may be Linear, Point or None.
- // In GL-style, only Min and Mag are specified, with the Mip filter
- // encoded as a combo setting in the Min filter. E.g.,
- // LinearMipmapLinear => Min Linear, Mip Linear,
- // LinearMipmapNearest => Min Linear, Mip Point,
- // Linear => Min Linear, Mip None (no mipmapping).
-
- // In order to sort this out, if the Mip filter is "unknown" (missing),
- // we assume GL-style specification, and extract the Mip setting from
- // the latter part of the GL-style Min setting. If the Mip filter is
- // specified, we assume a DX-style specification, and the three
- // components are assigned separately. Any GL-style combo
- // modes used in DX mode are ignored (only the first part is used).
-
- o3d_sampler->set_min_filter(ConvertFilterType(min_filter, false));
- o3d_sampler->set_mag_filter(ConvertFilterType(mag_filter, false));
-
- // If the mip filter is set to "UNKNOWN", we assume it's a GL-style
- // mode, and use the 2nd part of the Min filter for the mip type. Otherwise,
- // we use the first part.
- if (mip_filter == FUDaeTextureFilterFunction::UNKNOWN) {
- o3d_sampler->set_mip_filter(ConvertMipmapFilter(min_filter));
- } else {
- o3d_sampler->set_mip_filter(ConvertFilterType(mip_filter, true));
- }
-
- // TODO(o3d): Once FCollada supports COLLADA v1.5, turn this on:
- // o3d_sampler->set_max_anisotropy(max_anisotropy);
-}
-
-// Sets the value of a Param on the given ParamObject from an FCollada
-// standard-profile effect parameter. If the FCollada parameter
-// contains a texture, the sampler_param_name and channel is used to set
-// a Sampler Param in o3d from the surface. If not, the
-// color_param_name is used to create set a vector Param value.
-// Parameters:
-// effect_standard: The fixed-function FCollada effect from which
-// to retrieve the parameter value.
-// param_object: The ParamObject on which parameters will be set.
-// color_param_mame: The name of the param to set, if a vector.
-// sampler_param_mame: The name of the param to set, if a texture sampler.
-// color_param: The FCollada parameter from which to retrieve the
-// new value.
-// channel: The texture channel to use (if any).
-void Collada::SetParamFromStandardEffectParam(
- FCDEffectStandard* effect_standard,
- ParamObject* param_object,
- const char* color_param_name,
- const char* sampler_param_name,
- FCDEffectParameter* color_param,
- int channel) {
- if (effect_standard->GetTextureCount(channel) > 0) {
- FCDTexture* texture = effect_standard->GetTexture(channel, 0);
- FCDEffectParameterSampler* sampler = texture->GetSampler();
- SetParamFromFCEffectParam(param_object, sampler_param_name, sampler);
- } else if (color_param) {
- SetParamFromFCEffectParam(param_object, color_param_name, color_param);
- }
-}
-
-// Sets the values of a ParamObject parameters from a given FCollada material
-// node. If a corresponding ParamObject parameter is not found, the FCollada
-// parameter is ignored.
-// TODO(o3d): Should we ignore params not found? Maybe the user wants those for
-// things other than rendering.
-// Parameters:
-// material: The FCollada material node from which to retrieve values.
-// param_object: The ParamObject on which parameters will be set.
-void Collada::SetParamsFromMaterial(FCDMaterial* material,
- ParamObject* param_object) {
- size_t pcount = material->GetEffectParameterCount();
- // TODO(o3d): This test (for determining if we used the
- // programmable profile or the fixed-func profile) is not very robust.
- // Remove this once the Material changes are in.
- if (pcount > 0) {
- for (size_t i = 0; i < pcount; ++i) {
- FCDEffectParameter* p = material->GetEffectParameter(i);
- LOG_ASSERT(p);
- String param_name(p->GetReference());
- // Check for an effect binding
- FCDEffectProfileFX* profile_fx = FindProfileFX(material->GetEffect());
- if (profile_fx) {
- FCDEffectTechnique* technique = profile_fx->GetTechnique(0);
- if (technique->GetPassCount() > 0) {
- FCDEffectPass* pass = technique->GetPass(0);
- for (size_t j = 0; j < pass->GetShaderCount(); ++j) {
- FCDEffectPassShader* shader = pass->GetShader(j);
- FCDEffectPassBind* bind =
- shader->FindBindingReference(p->GetReference());
- if (bind) {
- param_name = *bind->symbol;
- break;
- }
- }
- }
- }
- SetParamFromFCEffectParam(param_object, param_name, p);
- }
- } else {
- FCDEffect* effect = material->GetEffect();
- FCDEffectStandard* effect_standard;
- if (effect && (effect_standard = static_cast<FCDEffectStandard *>(
- effect->FindProfile(FUDaeProfileType::COMMON))) != NULL) {
- SetParamFromStandardEffectParam(effect_standard,
- param_object,
- kMaterialParamNameEmissive,
- kMaterialParamNameEmissiveSampler,
- effect_standard->GetEmissionColorParam(),
- FUDaeTextureChannel::EMISSION);
- SetParamFromStandardEffectParam(effect_standard,
- param_object,
- kMaterialParamNameAmbient,
- kMaterialParamNameAmbientSampler,
- effect_standard->GetAmbientColorParam(),
- FUDaeTextureChannel::AMBIENT);
- SetParamFromStandardEffectParam(effect_standard,
- param_object,
- kMaterialParamNameDiffuse,
- kMaterialParamNameDiffuseSampler,
- effect_standard->GetDiffuseColorParam(),
- FUDaeTextureChannel::DIFFUSE);
- SetParamFromStandardEffectParam(effect_standard,
- param_object,
- kMaterialParamNameSpecular,
- kMaterialParamNameSpecularSampler,
- effect_standard->GetSpecularColorParam(),
- FUDaeTextureChannel::SPECULAR);
- SetParamFromStandardEffectParam(effect_standard,
- param_object,
- NULL,
- kMaterialParamNameBumpSampler,
- NULL,
- FUDaeTextureChannel::BUMP);
- SetParamFromFCEffectParam(param_object,
- kMaterialParamNameShininess,
- effect_standard->GetShininessParam());
- SetParamFromFCEffectParam(param_object,
- kMaterialParamNameSpecularFactor,
- effect_standard->GetSpecularFactorParam());
- }
- }
-}
-} // namespace o3d