mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-04 02:42:49 -04:00
3010 lines
104 KiB
C++
3010 lines
104 KiB
C++
// Filename: mayaToEggConverter.cxx
|
|
// Created by: drose (10Nov99)
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
//
|
|
// PANDA 3D SOFTWARE
|
|
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
|
|
//
|
|
// All use of this software is subject to the terms of the Panda 3d
|
|
// Software license. You should have received a copy of this license
|
|
// along with this source code; you will also find a current copy of
|
|
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
|
|
//
|
|
// To contact the maintainers of this program write to
|
|
// panda3d-general@lists.sourceforge.net .
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
#include "mayaToEggConverter.h"
|
|
#include "mayaShader.h"
|
|
#include "maya_funcs.h"
|
|
#include "config_mayaegg.h"
|
|
#include "mayaEggGroupUserData.h"
|
|
|
|
#include "eggData.h"
|
|
#include "eggGroup.h"
|
|
#include "eggTable.h"
|
|
#include "eggVertex.h"
|
|
#include "eggVertexPool.h"
|
|
#include "eggNurbsSurface.h"
|
|
#include "eggNurbsCurve.h"
|
|
#include "eggPolygon.h"
|
|
#include "eggPrimitive.h"
|
|
#include "eggTexture.h"
|
|
#include "eggTextureCollection.h"
|
|
#include "eggXfmSAnim.h"
|
|
#include "eggSAnimData.h"
|
|
#include "string_utils.h"
|
|
#include "dcast.h"
|
|
|
|
#include "pre_maya_include.h"
|
|
#include <maya/MArgList.h>
|
|
#include <maya/MColor.h>
|
|
#include <maya/MDagPath.h>
|
|
#include <maya/MFnCamera.h>
|
|
#include <maya/MFnDagNode.h>
|
|
#include <maya/MFnTransform.h>
|
|
#include <maya/MFnLight.h>
|
|
#include <maya/MFnNurbsSurface.h>
|
|
#include <maya/MFnNurbsCurve.h>
|
|
#include <maya/MFnMesh.h>
|
|
#include <maya/MFnMeshData.h>
|
|
#include <maya/MFnPlugin.h>
|
|
#include <maya/MItDag.h>
|
|
#include <maya/MLibrary.h>
|
|
#include <maya/MMatrix.h>
|
|
#include <maya/MObject.h>
|
|
#include <maya/MPoint.h>
|
|
#include <maya/MPointArray.h>
|
|
#include <maya/MDoubleArray.h>
|
|
#include <maya/MIntArray.h>
|
|
#include <maya/MPxCommand.h>
|
|
#include <maya/MStatus.h>
|
|
#include <maya/MString.h>
|
|
#include <maya/MTransformationMatrix.h>
|
|
#include <maya/MVector.h>
|
|
#include <maya/MTesselationParams.h>
|
|
#include <maya/MAnimControl.h>
|
|
#include <maya/MGlobal.h>
|
|
#include <maya/MAnimUtil.h>
|
|
#include <maya/MFnSkinCluster.h>
|
|
#include <maya/MFnSingleIndexedComponent.h>
|
|
#include <maya/MFnDoubleIndexedComponent.h>
|
|
#include <maya/MFnBlendShapeDeformer.h>
|
|
#include <maya/MItDependencyGraph.h>
|
|
#include <maya/MDagPathArray.h>
|
|
#include <maya/MSelectionList.h>
|
|
#include "post_maya_include.h"
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: MayaToEggConverter::Constructor
|
|
// Access: Public
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
MayaToEggConverter::
|
|
MayaToEggConverter(const string &program_name) :
|
|
_program_name(program_name),
|
|
_tree(this)
|
|
{
|
|
// Make sure the library is properly initialized.
|
|
init_libmayaegg();
|
|
|
|
_from_selection = false;
|
|
|
|
_polygon_output = false;
|
|
_polygon_tolerance = 0.01;
|
|
_respect_maya_double_sided = maya_default_double_sided;
|
|
_always_show_vertex_color = maya_default_vertex_color;
|
|
_keep_all_uvsets = false;
|
|
_round_uvs = false;
|
|
_transform_type = TT_model;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: MayaToEggConverter::Copy Constructor
|
|
// Access: Public
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
MayaToEggConverter::
|
|
MayaToEggConverter(const MayaToEggConverter ©) :
|
|
_program_name(copy._program_name),
|
|
_from_selection(copy._from_selection),
|
|
_subsets(copy._subsets),
|
|
_subroots(copy._subroots),
|
|
_ignore_sliders(copy._ignore_sliders),
|
|
_force_joints(copy._force_joints),
|
|
_tree(this),
|
|
_maya(copy._maya),
|
|
_polygon_output(copy._polygon_output),
|
|
_polygon_tolerance(copy._polygon_tolerance),
|
|
_respect_maya_double_sided(copy._respect_maya_double_sided),
|
|
_always_show_vertex_color(copy._always_show_vertex_color),
|
|
_keep_all_uvsets(copy._keep_all_uvsets),
|
|
_round_uvs(copy._round_uvs),
|
|
_transform_type(copy._transform_type)
|
|
{
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: MayaToEggConverter::Destructor
|
|
// Access: Public, Virtual
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
MayaToEggConverter::
|
|
~MayaToEggConverter() {
|
|
close_api();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: MayaToEggConverter::make_copy
|
|
// Access: Public, Virtual
|
|
// Description: Allocates and returns a new copy of the converter.
|
|
////////////////////////////////////////////////////////////////////
|
|
SomethingToEggConverter *MayaToEggConverter::
|
|
make_copy() {
|
|
return new MayaToEggConverter(*this);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: MayaToEggConverter::get_name
|
|
// Access: Public, Virtual
|
|
// Description: Returns the English name of the file type this
|
|
// converter supports.
|
|
////////////////////////////////////////////////////////////////////
|
|
string MayaToEggConverter::
|
|
get_name() const {
|
|
return "Maya";
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: MayaToEggConverter::get_extension
|
|
// Access: Public, Virtual
|
|
// Description: Returns the common extension of the file type this
|
|
// converter supports.
|
|
////////////////////////////////////////////////////////////////////
|
|
string MayaToEggConverter::
|
|
get_extension() const {
|
|
return "mb";
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: MayaToEggConverter::get_additional_extensions
|
|
// Access: Public, Virtual
|
|
// Description: Returns a space-separated list of extension, in
|
|
// addition to the one returned by get_extension(), that
|
|
// are recognized by this converter.
|
|
////////////////////////////////////////////////////////////////////
|
|
string MayaToEggConverter::
|
|
get_additional_extensions() const {
|
|
return "ma";
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: MayaToEggConverter::convert_file
|
|
// Access: Public, Virtual
|
|
// Description: Handles the reading of the input file and converting
|
|
// it to egg. Returns true if successful, false
|
|
// otherwise.
|
|
//
|
|
// This is designed to be as generic as possible,
|
|
// generally in support of run-time loading.
|
|
// Also see convert_maya().
|
|
////////////////////////////////////////////////////////////////////
|
|
bool MayaToEggConverter::
|
|
convert_file(const Filename &filename) {
|
|
if (!open_api()) {
|
|
mayaegg_cat.error()
|
|
<< "Maya is not available.\n";
|
|
return false;
|
|
}
|
|
|
|
// We must ensure our Maya pointers are cleared before we reset the
|
|
// Maya scene, because resetting the Maya scene will invalidate all
|
|
// the Maya pointers we are holding and cause a crash if we try to
|
|
// free them later.
|
|
clear();
|
|
|
|
if (!_maya->read(filename)) {
|
|
mayaegg_cat.error()
|
|
<< "Unable to read " << filename << "\n";
|
|
return false;
|
|
}
|
|
|
|
if (_character_name.empty()) {
|
|
_character_name = filename.get_basename_wo_extension();
|
|
}
|
|
|
|
return convert_maya();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: MayaToEggConverter::clear_subroots
|
|
// Access: Public
|
|
// Description: Empties the list of subroot nodes added via
|
|
// add_subroot(). The entire file will once again be
|
|
// converted.
|
|
////////////////////////////////////////////////////////////////////
|
|
void MayaToEggConverter::
|
|
clear_subroots() {
|
|
_subroots.clear();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: MayaToEggConverter::add_subroot
|
|
// Access: Public
|
|
// Description: Adds a name pattern to the list of subroot nodes. If
|
|
// the list of subroot nodes is not empty, then only a
|
|
// subroot of the nodes in the maya file will be
|
|
// converted: those whose names match one of the
|
|
// patterns given on this list.
|
|
////////////////////////////////////////////////////////////////////
|
|
void MayaToEggConverter::
|
|
add_subroot(const GlobPattern &glob) {
|
|
_subroots.push_back(glob);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: MayaToEggConverter::clear_subsets
|
|
// Access: Public
|
|
// Description: Empties the list of subset nodes added via
|
|
// add_subset(). The entire file will once again be
|
|
// converted.
|
|
////////////////////////////////////////////////////////////////////
|
|
void MayaToEggConverter::
|
|
clear_subsets() {
|
|
_subsets.clear();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: MayaToEggConverter::add_subset
|
|
// Access: Public
|
|
// Description: Adds a name pattern to the list of subset nodes. If
|
|
// the list of subset nodes is not empty, then only a
|
|
// subset of the nodes in the maya file will be
|
|
// converted: those whose names match one of the
|
|
// patterns given on this list.
|
|
////////////////////////////////////////////////////////////////////
|
|
void MayaToEggConverter::
|
|
add_subset(const GlobPattern &glob) {
|
|
_subsets.push_back(glob);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: MayaToEggConverter::clear_ignore_sliders
|
|
// Access: Public
|
|
// Description: Empties the list of ignore_sliders added via
|
|
// add_ignore_slider(). No sliders will be ignored.
|
|
////////////////////////////////////////////////////////////////////
|
|
void MayaToEggConverter::
|
|
clear_ignore_sliders() {
|
|
_ignore_sliders.clear();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: MayaToEggConverter::add_ignore_slider
|
|
// Access: Public
|
|
// Description: Adds a name pattern to the list of ignore_sliders.
|
|
// Any slider (blend shape deformer) that matches a name
|
|
// on the list will not be converted or otherwise
|
|
// molested by the converter. This is occasionally
|
|
// necessary to filter out automatically-created sliders
|
|
// that are not intended to be used directly, but
|
|
// instead have an indirect effect on other sliders.
|
|
////////////////////////////////////////////////////////////////////
|
|
void MayaToEggConverter::
|
|
add_ignore_slider(const GlobPattern &glob) {
|
|
_ignore_sliders.push_back(glob);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: MayaToEggConverter::ignore_slider
|
|
// Access: Public
|
|
// Description: Returns true if the indicated name is on the list of
|
|
// sliders to ignore, false otherwise.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool MayaToEggConverter::
|
|
ignore_slider(const string &name) const {
|
|
Globs::const_iterator gi;
|
|
for (gi = _ignore_sliders.begin(); gi != _ignore_sliders.end(); ++gi) {
|
|
if ((*gi).matches(name)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: MayaToEggConverter::clear_force_joints
|
|
// Access: Public
|
|
// Description: Empties the list of force_joints added via
|
|
// add_force_joint(). No joints will be forced.
|
|
////////////////////////////////////////////////////////////////////
|
|
void MayaToEggConverter::
|
|
clear_force_joints() {
|
|
_force_joints.clear();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: MayaToEggConverter::add_force_joint
|
|
// Access: Public
|
|
// Description: Adds a name pattern to the list of force_joints.
|
|
//
|
|
// Any DAG node that matches a name on the list will be
|
|
// treated as if it were a joint during the conversion
|
|
// process; it will receive animation and position
|
|
// information. Normally, a true Maya joint, as well as
|
|
// any DAG nodes whose transforms are animated, will
|
|
// automatically be flagged as a Panda joint.
|
|
////////////////////////////////////////////////////////////////////
|
|
void MayaToEggConverter::
|
|
add_force_joint(const GlobPattern &glob) {
|
|
_force_joints.push_back(glob);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: MayaToEggConverter::force_joint
|
|
// Access: Public
|
|
// Description: Returns true if the indicated name is on the list of
|
|
// DAG nodes to treat as a joint, false otherwise.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool MayaToEggConverter::
|
|
force_joint(const string &name) const {
|
|
Globs::const_iterator gi;
|
|
for (gi = _force_joints.begin(); gi != _force_joints.end(); ++gi) {
|
|
if ((*gi).matches(name)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: MayaToEggConverter::set_from_selection
|
|
// Access: Public
|
|
// Description: Sets the flag that indicates whether the currently
|
|
// selected Maya geometry will be converted. If this is
|
|
// true, and the selection is nonempty, then only the
|
|
// selected geometry will be converted. If this is
|
|
// false, the entire file will be converted.
|
|
////////////////////////////////////////////////////////////////////
|
|
void MayaToEggConverter::
|
|
set_from_selection(bool from_selection) {
|
|
_from_selection = from_selection;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: MayaToEggConverter::get_input_units
|
|
// Access: Public, Virtual
|
|
// Description: This may be called after convert_file() has been
|
|
// called and returned true, indicating a successful
|
|
// conversion. It will return the distance units
|
|
// represented by the converted egg file, if known, or
|
|
// DU_invalid if not known.
|
|
////////////////////////////////////////////////////////////////////
|
|
DistanceUnit MayaToEggConverter::
|
|
get_input_units() {
|
|
return _maya->get_units();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: MayaToEggConverter::convert_maya
|
|
// Access: Public
|
|
// Description: Fills up the egg_data structure according to the
|
|
// global maya model data. Returns true if successful,
|
|
// false if there is an error.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool MayaToEggConverter::
|
|
convert_maya() {
|
|
clear();
|
|
clear_error();
|
|
|
|
if (!open_api()) {
|
|
mayaegg_cat.error()
|
|
<< "Maya is not available.\n";
|
|
return false;
|
|
}
|
|
|
|
if (_egg_data->get_coordinate_system() == CS_default) {
|
|
_egg_data->set_coordinate_system(_maya->get_coordinate_system());
|
|
}
|
|
|
|
mayaegg_cat.info()
|
|
<< "Converting from Maya.\n";
|
|
|
|
// Figure out the animation parameters.
|
|
double start_frame, end_frame, frame_inc, input_frame_rate, output_frame_rate;
|
|
if (has_start_frame()) {
|
|
start_frame = get_start_frame();
|
|
} else {
|
|
start_frame = MAnimControl::minTime().value();
|
|
}
|
|
if (has_end_frame()) {
|
|
end_frame = get_end_frame();
|
|
} else {
|
|
end_frame = MAnimControl::maxTime().value();
|
|
}
|
|
if (has_frame_inc()) {
|
|
frame_inc = get_frame_inc();
|
|
} else {
|
|
frame_inc = 1.0;
|
|
}
|
|
if (has_input_frame_rate()) {
|
|
input_frame_rate = get_input_frame_rate();
|
|
} else {
|
|
MTime time(1.0, MTime::kSeconds);
|
|
input_frame_rate = time.as(MTime::uiUnit());
|
|
}
|
|
if (has_output_frame_rate()) {
|
|
output_frame_rate = get_output_frame_rate();
|
|
} else {
|
|
output_frame_rate = input_frame_rate;
|
|
}
|
|
|
|
frame_inc = frame_inc * input_frame_rate / output_frame_rate;
|
|
|
|
bool all_ok = _tree.build_hierarchy();
|
|
|
|
if (all_ok) {
|
|
if (!_subroots.empty()) {
|
|
Globs::const_iterator gi;
|
|
for (gi = _subroots.begin(); gi != _subroots.end(); ++gi) {
|
|
if (!_tree.tag_joint_named(*gi)) {
|
|
mayaegg_cat.info()
|
|
<< "No node matching " << *gi << " found.\n";
|
|
}
|
|
}
|
|
|
|
} else {
|
|
// This call makes every node a potential joint; but it does not
|
|
// necessarily force nodes to be joints.
|
|
_tree.tag_joint_all();
|
|
}
|
|
}
|
|
|
|
if (all_ok) {
|
|
if (_from_selection) {
|
|
all_ok = _tree.tag_selected();
|
|
|
|
} else if (!_subsets.empty()) {
|
|
Globs::const_iterator gi;
|
|
for (gi = _subsets.begin(); gi != _subsets.end(); ++gi) {
|
|
if (!_tree.tag_named(*gi)) {
|
|
mayaegg_cat.info()
|
|
<< "No node matching " << *gi << " found.\n";
|
|
}
|
|
}
|
|
|
|
} else {
|
|
_tree.tag_all();
|
|
}
|
|
}
|
|
|
|
if (all_ok) {
|
|
switch (get_animation_convert()) {
|
|
case AC_pose:
|
|
// pose: set to a specific frame, then get out the static geometry.
|
|
mayaegg_cat.info(false)
|
|
<< "frame " << start_frame << "\n";
|
|
MGlobal::viewFrame(MTime(start_frame, MTime::uiUnit()));
|
|
// fall through
|
|
|
|
case AC_none:
|
|
// none: just get out a static model, no animation.
|
|
mayaegg_cat.info() << "ac_none" << endl;
|
|
all_ok = convert_hierarchy(get_egg_data());
|
|
break;
|
|
|
|
case AC_flip:
|
|
case AC_strobe:
|
|
// flip or strobe: get out a series of static models, one per
|
|
// frame, under a sequence node for AC_flip.
|
|
all_ok = convert_flip(start_frame, end_frame, frame_inc,
|
|
output_frame_rate);
|
|
break;
|
|
|
|
case AC_model:
|
|
// model: get out an animatable model with joints and vertex
|
|
// membership.
|
|
all_ok = convert_char_model();
|
|
break;
|
|
|
|
case AC_chan:
|
|
// chan: get out a series of animation tables.
|
|
all_ok = convert_char_chan(start_frame, end_frame, frame_inc,
|
|
output_frame_rate);
|
|
break;
|
|
|
|
case AC_both:
|
|
// both: Put a model and its animation into the same egg file.
|
|
_animation_convert = AC_model;
|
|
if (!convert_char_model()) {
|
|
all_ok = false;
|
|
}
|
|
_animation_convert = AC_chan;
|
|
if (!convert_char_chan(start_frame, end_frame, frame_inc,
|
|
output_frame_rate)) {
|
|
all_ok = false;
|
|
}
|
|
break;
|
|
|
|
case AC_invalid:
|
|
break;
|
|
};
|
|
|
|
reparent_decals(get_egg_data());
|
|
}
|
|
|
|
if (had_error()) {
|
|
all_ok = false;
|
|
}
|
|
|
|
if (all_ok) {
|
|
mayaegg_cat.info()
|
|
<< "Converted, no errors.\n";
|
|
} else {
|
|
mayaegg_cat.info()
|
|
<< "Errors encountered in conversion.\n";
|
|
}
|
|
|
|
return all_ok;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: MayaToEggConverter::open_api
|
|
// Access: Public
|
|
// Description: Attempts to open the Maya API if it was not already
|
|
// open, and returns true if successful, or false if
|
|
// there is an error.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool MayaToEggConverter::
|
|
open_api() {
|
|
if (_maya == (MayaApi *)NULL || !_maya->is_valid()) {
|
|
_maya = MayaApi::open_api(_program_name);
|
|
}
|
|
return _maya->is_valid();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: MayaToEggConverter::close_api
|
|
// Access: Public
|
|
// Description: Closes the Maya API, if it was previously opened.
|
|
// Caution! Maya appears to call exit() when its API is
|
|
// closed.
|
|
////////////////////////////////////////////////////////////////////
|
|
void MayaToEggConverter::
|
|
close_api() {
|
|
// We have to clear the shaders, at least, before we release the
|
|
// Maya API.
|
|
clear();
|
|
_maya.clear();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: MayaToEggConverter::clear
|
|
// Access: Public
|
|
// Description: Frees all of the Maya pointers kept within this
|
|
// object, in preparation for loading a new scene or
|
|
// releasing the Maya API.
|
|
////////////////////////////////////////////////////////////////////
|
|
void MayaToEggConverter::
|
|
clear() {
|
|
_tree.clear();
|
|
_textures.clear();
|
|
_shaders.clear();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: MayaToEggConverter::convert_flip
|
|
// Access: Private
|
|
// Description: Converts the animation as a series of models that
|
|
// cycle (flip) from one to the next at the appropriate
|
|
// frame rate. This is the most likely to convert
|
|
// precisely (since we ask Maya to tell us the vertex
|
|
// position each time) but it is the most wasteful in
|
|
// terms of memory utilization (since a complete of the
|
|
// model is stored for each frame).
|
|
////////////////////////////////////////////////////////////////////
|
|
bool MayaToEggConverter::
|
|
convert_flip(double start_frame, double end_frame, double frame_inc,
|
|
double output_frame_rate) {
|
|
bool all_ok = true;
|
|
|
|
EggGroup *sequence_node = new EggGroup(_character_name);
|
|
get_egg_data()->add_child(sequence_node);
|
|
if (_animation_convert == AC_flip) {
|
|
sequence_node->set_switch_flag(true);
|
|
sequence_node->set_switch_fps(output_frame_rate);
|
|
}
|
|
|
|
MTime frame(start_frame, MTime::uiUnit());
|
|
MTime frame_stop(end_frame, MTime::uiUnit());
|
|
while (frame <= frame_stop) {
|
|
mayaegg_cat.info(false)
|
|
<< "frame " << frame.value() << "\n";
|
|
ostringstream name_strm;
|
|
name_strm << "frame" << frame.value();
|
|
EggGroup *frame_root = new EggGroup(name_strm.str());
|
|
sequence_node->add_child(frame_root);
|
|
|
|
MGlobal::viewFrame(frame);
|
|
if (!convert_hierarchy(frame_root)) {
|
|
all_ok = false;
|
|
}
|
|
|
|
frame += frame_inc;
|
|
}
|
|
|
|
return all_ok;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: MayaToEggConverter::convert_char_model
|
|
// Access: Private
|
|
// Description: Converts the file as an animatable character
|
|
// model, with joints and vertex membership.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool MayaToEggConverter::
|
|
convert_char_model() {
|
|
if (has_neutral_frame()) {
|
|
MTime frame(get_neutral_frame(), MTime::uiUnit());
|
|
mayaegg_cat.info(false)
|
|
<< "neutral frame " << frame.value() << "\n";
|
|
MGlobal::viewFrame(frame);
|
|
}
|
|
|
|
// It's also important for us to reset all the blend shape sliders
|
|
// to 0 before we get out the model. Otherwise, the model we
|
|
// convert will have the current positions of the sliders baked in.
|
|
_tree.reset_sliders();
|
|
|
|
EggGroup *char_node = new EggGroup(_character_name);
|
|
get_egg_data()->add_child(char_node);
|
|
char_node->set_dart_type(EggGroup::DT_default);
|
|
|
|
return convert_hierarchy(char_node);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: MayaToEggConverter::convert_char_chan
|
|
// Access: Private
|
|
// Description: Converts the animation as a series of tables to apply
|
|
// to the character model, as retrieved earlier via
|
|
// AC_model.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool MayaToEggConverter::
|
|
convert_char_chan(double start_frame, double end_frame, double frame_inc,
|
|
double output_frame_rate) {
|
|
// MStatus status;
|
|
|
|
EggTable *root_table_node = new EggTable();
|
|
get_egg_data()->add_child(root_table_node);
|
|
EggTable *bundle_node = new EggTable(_character_name);
|
|
bundle_node->set_table_type(EggTable::TT_bundle);
|
|
root_table_node->add_child(bundle_node);
|
|
EggTable *skeleton_node = new EggTable("<skeleton>");
|
|
bundle_node->add_child(skeleton_node);
|
|
EggTable *morph_node = new EggTable("morph");
|
|
bundle_node->add_child(morph_node);
|
|
|
|
// Set the frame rate before we start asking for anim tables to be
|
|
// created.
|
|
_tree._fps = output_frame_rate;
|
|
_tree.clear_egg(get_egg_data(), NULL, skeleton_node, morph_node);
|
|
|
|
// Now we can get the animation data by walking through all of the
|
|
// frames, one at a time, and getting the joint angles at each
|
|
// frame.
|
|
|
|
// This is just a temporary EggGroup to receive the transform for
|
|
// each joint each frame.
|
|
PT(EggGroup) tgroup = new EggGroup;
|
|
|
|
int num_nodes = _tree.get_num_nodes();
|
|
int num_sliders = _tree.get_num_blend_descs();
|
|
int i;
|
|
|
|
MTime frame(start_frame, MTime::uiUnit());
|
|
MTime frame_stop(end_frame, MTime::uiUnit());
|
|
while (frame <= frame_stop) {
|
|
if (mayaegg_cat.is_debug()) {
|
|
mayaegg_cat.debug(false)
|
|
<< "frame " << frame.value() << "\n";
|
|
} else {
|
|
// We have to write to cerr instead of mayaegg_cat to allow
|
|
// flushing without writing a newline.
|
|
cerr << "." << flush;
|
|
}
|
|
MGlobal::viewFrame(frame);
|
|
|
|
for (i = 0; i < num_nodes; i++) {
|
|
MayaNodeDesc *node_desc = _tree.get_node(i);
|
|
if (node_desc->is_joint()) {
|
|
if (mayaegg_cat.is_spam()) {
|
|
mayaegg_cat.spam()
|
|
<< "joint " << node_desc->get_name() << "\n";
|
|
}
|
|
get_joint_transform(node_desc->get_dag_path(), tgroup);
|
|
EggXfmSAnim *anim = _tree.get_egg_anim(node_desc);
|
|
if (!anim->add_data(tgroup->get_transform3d())) {
|
|
mayaegg_cat.error()
|
|
<< "Invalid transform on " << node_desc->get_name()
|
|
<< " frame " << frame.value() << ".\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < num_sliders; i++) {
|
|
MayaBlendDesc *blend_desc = _tree.get_blend_desc(i);
|
|
if (mayaegg_cat.is_spam()) {
|
|
mayaegg_cat.spam()
|
|
<< "slider " << blend_desc->get_name() << "\n";
|
|
}
|
|
EggSAnimData *anim = _tree.get_egg_slider(blend_desc);
|
|
anim->add_data(blend_desc->get_slider());
|
|
}
|
|
|
|
frame += frame_inc;
|
|
}
|
|
|
|
// Now optimize all of the tables we just filled up, for no real
|
|
// good reason, except that it makes the resulting egg file a little
|
|
// easier to read.
|
|
for (i = 0; i < num_nodes; i++) {
|
|
MayaNodeDesc *node_desc = _tree.get_node(i);
|
|
if (node_desc->is_joint()) {
|
|
_tree.get_egg_anim(node_desc)->optimize();
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < num_sliders; i++) {
|
|
MayaBlendDesc *blend_desc = _tree.get_blend_desc(i);
|
|
EggSAnimData *anim = _tree.get_egg_slider(blend_desc);
|
|
anim->optimize();
|
|
}
|
|
|
|
mayaegg_cat.info(false)
|
|
<< "\n";
|
|
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: MayaToEggConverter::convert_hierarchy
|
|
// Access: Private
|
|
// Description: Generates egg structures for each node in the Maya
|
|
// hierarchy.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool MayaToEggConverter::
|
|
convert_hierarchy(EggGroupNode *egg_root) {
|
|
int num_nodes = _tree.get_num_nodes();
|
|
|
|
if (_round_uvs) {
|
|
mayaegg_cat.info() << "will round up uv coordinates" << endl;
|
|
}
|
|
|
|
if (_keep_all_uvsets) {
|
|
mayaegg_cat.info() << "will keep_all_uvsets" << endl;
|
|
}
|
|
|
|
_tree.clear_egg(get_egg_data(), egg_root, NULL, NULL);
|
|
for (int i = 0; i < num_nodes; i++) {
|
|
MayaNodeDesc *node = _tree.get_node(i);
|
|
if (!process_model_node(node)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: MayaToEggConverter::process_model_node
|
|
// Access: Private
|
|
// Description: Converts the indicated Maya node (given a MDagPath,
|
|
// similar in concept to Panda's NodePath) to the
|
|
// corresponding Egg structure. Returns true if
|
|
// successful, false if an error was encountered.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool MayaToEggConverter::
|
|
process_model_node(MayaNodeDesc *node_desc) {
|
|
if (!node_desc->has_dag_path()) {
|
|
// If the node has no Maya equivalent, never mind.
|
|
return true;
|
|
}
|
|
|
|
MDagPath dag_path = node_desc->get_dag_path();
|
|
|
|
MStatus status;
|
|
MFnDagNode dag_node(dag_path, &status);
|
|
if (!status) {
|
|
status.perror("MFnDagNode constructor");
|
|
mayaegg_cat.error() << dag_path.fullPathName().asChar() << "\n";
|
|
return false;
|
|
}
|
|
|
|
MObject node = dag_path.transform(&status);
|
|
if (!status) {
|
|
status.perror("dag_path.transform()");
|
|
return false;
|
|
}
|
|
|
|
string path = dag_path.fullPathName().asChar();
|
|
|
|
if (mayaegg_cat.is_debug()) {
|
|
mayaegg_cat.debug()
|
|
<< path << ": " << dag_node.typeName().asChar();
|
|
|
|
if (MAnimUtil::isAnimated(dag_path)) {
|
|
mayaegg_cat.debug(false)
|
|
<< " (animated)";
|
|
}
|
|
|
|
mayaegg_cat.debug(false) << "\n";
|
|
}
|
|
|
|
if (dag_node.inUnderWorld()) {
|
|
if (mayaegg_cat.is_debug()) {
|
|
mayaegg_cat.debug()
|
|
<< "Ignoring underworld node " << path
|
|
<< "\n";
|
|
}
|
|
|
|
} else if (dag_node.isIntermediateObject()) {
|
|
if (mayaegg_cat.is_debug()) {
|
|
mayaegg_cat.debug()
|
|
<< "Ignoring intermediate object " << path
|
|
<< "\n";
|
|
}
|
|
|
|
} else if (dag_path.hasFn(MFn::kCamera)) {
|
|
if (mayaegg_cat.is_debug()) {
|
|
mayaegg_cat.debug()
|
|
<< "Ignoring camera node " << path
|
|
<< "\n";
|
|
}
|
|
|
|
MFnCamera camera (dag_path, &status);
|
|
if ( !status ) {
|
|
status.perror("MFnCamera constructor");
|
|
mayaegg_cat.error() << "camera extraction failed" << endl;
|
|
return false;
|
|
}
|
|
|
|
// Extract some interesting Camera data
|
|
if (mayaegg_cat.is_spam()) {
|
|
mayaegg_cat.spam() << " eyePoint: "
|
|
<< camera.eyePoint(MSpace::kWorld) << endl;
|
|
mayaegg_cat.spam() << " upDirection: "
|
|
<< camera.upDirection(MSpace::kWorld) << endl;
|
|
mayaegg_cat.spam() << " viewDirection: "
|
|
<< camera.viewDirection(MSpace::kWorld) << endl;
|
|
mayaegg_cat.spam() << " aspectRatio: " << camera.aspectRatio() << endl;
|
|
mayaegg_cat.spam() << " horizontalFilmAperture: "
|
|
<< camera.horizontalFilmAperture() << endl;
|
|
mayaegg_cat.spam() << " verticalFilmAperture: "
|
|
<< camera.verticalFilmAperture() << endl;
|
|
}
|
|
|
|
} else if (dag_path.hasFn(MFn::kLight)) {
|
|
if (mayaegg_cat.is_debug()) {
|
|
mayaegg_cat.debug()
|
|
<< "Ignoring light node " << path
|
|
<< "\n";
|
|
}
|
|
/*
|
|
MFnLight light (dag_path, &status);
|
|
if ( !status ) {
|
|
status.perror("MFnLight constructor");
|
|
mayaegg_cat.error() << "light extraction failed" << endl;
|
|
return false;
|
|
}
|
|
|
|
// Get the translation/rotation/scale data
|
|
//printTransformData(dag_path, quiet);
|
|
|
|
// Extract some interesting Light data
|
|
MColor color;
|
|
|
|
color = light.color();
|
|
cout << " color: ["
|
|
<< color.r << ", "
|
|
<< color.g << ", "
|
|
<< color.b << "]\n";
|
|
color = light.shadowColor();
|
|
cout << " shadowColor: ["
|
|
<< color.r << ", "
|
|
<< color.g << ", "
|
|
<< color.b << "]\n";
|
|
|
|
cout << " intensity: " << light.intensity() << endl;
|
|
|
|
} else if (dag_path.hasFn(MFn::kNurbsSurface)) {
|
|
EggGroup *egg_group = _tree.get_egg_group(node_desc);
|
|
get_transform(node_desc, dag_path, egg_group);
|
|
|
|
if (node_desc->is_tagged()) {
|
|
MFnNurbsSurface surface(dag_path, &status);
|
|
if (!status) {
|
|
mayaegg_cat.info()
|
|
<< "Error in node " << path
|
|
<< ":\n"
|
|
<< " it appears to have a NURBS surface, but does not.\n";
|
|
} else {
|
|
make_nurbs_surface(node_desc, dag_path, surface, egg_group);
|
|
}
|
|
}
|
|
*/
|
|
} else if (dag_path.hasFn(MFn::kNurbsCurve)) {
|
|
// Only convert NurbsCurves if we aren't making an animated model.
|
|
// Animated models, as a general rule, don't want these sorts of
|
|
// things in them.
|
|
if (_animation_convert != AC_model) {
|
|
EggGroup *egg_group = _tree.get_egg_group(node_desc);
|
|
get_transform(node_desc, dag_path, egg_group);
|
|
|
|
if (node_desc->is_tagged()) {
|
|
MFnNurbsCurve curve(dag_path, &status);
|
|
if (!status) {
|
|
mayaegg_cat.info()
|
|
<< "Error in node " << path << ":\n"
|
|
<< " it appears to have a NURBS curve, but does not.\n";
|
|
} else {
|
|
make_nurbs_curve(dag_path, curve, egg_group);
|
|
}
|
|
}
|
|
}
|
|
|
|
} else if (dag_path.hasFn(MFn::kMesh)) {
|
|
if (node_desc->is_tagged()) {
|
|
EggGroup *egg_group = _tree.get_egg_group(node_desc);
|
|
get_transform(node_desc, dag_path, egg_group);
|
|
MFnMesh mesh(dag_path, &status);
|
|
if (!status) {
|
|
mayaegg_cat.info()
|
|
<< "Error in node " << path << ":\n"
|
|
<< " it appears to have a polygon mesh, but does not.\n";
|
|
} else {
|
|
make_polyset(node_desc, dag_path, mesh, egg_group);
|
|
}
|
|
}
|
|
/*
|
|
EggGroup *egg_group = _tree.get_egg_group(node_desc);
|
|
get_transform(node_desc, dag_path, egg_group);
|
|
|
|
if (node_desc->is_tagged()) {
|
|
MFnMesh mesh(dag_path, &status);
|
|
if (!status) {
|
|
mayaegg_cat.info()
|
|
<< "Error in node " << path << ":\n"
|
|
<< " it appears to have a polygon mesh, but does not.\n";
|
|
} else {
|
|
make_polyset(node_desc, dag_path, mesh, egg_group);
|
|
}
|
|
}
|
|
*/
|
|
} else if (dag_path.hasFn(MFn::kLocator)) {
|
|
if (_animation_convert == AC_none) {
|
|
if (!node_desc->is_tagged()) {
|
|
return true;
|
|
}
|
|
}
|
|
EggGroup *egg_group = _tree.get_egg_group(node_desc);
|
|
|
|
if (mayaegg_cat.is_debug()) {
|
|
mayaegg_cat.debug()
|
|
<< "Locator at " << path << "\n";
|
|
}
|
|
|
|
if (node_desc->is_tagged()) {
|
|
// Presumably, the locator's position has some meaning to the
|
|
// end-user, so we will implicitly tag it with the DCS flag so it
|
|
// won't get flattened out.
|
|
if (_animation_convert != AC_model) {
|
|
// For now, don't set the DCS flag on locators within
|
|
// character models, since egg-optchar doesn't understand
|
|
// this. Perhaps there's no reason to ever change this, since
|
|
// locators within character models may not be meaningful.
|
|
egg_group->set_dcs_type(EggGroup::DC_net);
|
|
}
|
|
get_transform(node_desc, dag_path, egg_group);
|
|
make_locator(dag_path, dag_node, egg_group);
|
|
}
|
|
|
|
} else {
|
|
// Just a generic node.
|
|
if (_animation_convert == AC_none) {
|
|
if (!node_desc->is_tagged()) {
|
|
return true;
|
|
}
|
|
}
|
|
EggGroup *egg_group = _tree.get_egg_group(node_desc);
|
|
get_transform(node_desc, dag_path, egg_group);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: MayaToEggConverter::get_transform
|
|
// Access: Private
|
|
// Description: Extracts the transform on the indicated Maya node,
|
|
// and applies it to the corresponding Egg node.
|
|
////////////////////////////////////////////////////////////////////
|
|
void MayaToEggConverter::
|
|
get_transform(MayaNodeDesc *node_desc, const MDagPath &dag_path,
|
|
EggGroup *egg_group) {
|
|
if (_animation_convert == AC_model) {
|
|
// When we're getting an animated model, we only get transforms
|
|
// for joints, and they get converted in a special way.
|
|
|
|
if (node_desc->is_joint()) {
|
|
if (mayaegg_cat.is_spam()) {
|
|
mayaegg_cat.spam()
|
|
<< "gt: joint " << node_desc->get_name() << "\n";
|
|
}
|
|
get_joint_transform(dag_path, egg_group);
|
|
}
|
|
return;
|
|
}
|
|
|
|
MStatus status;
|
|
MObject transformNode = dag_path.transform(&status);
|
|
if (!status && status.statusCode() == MStatus::kInvalidParameter) {
|
|
// This node has no transform - i.e., it's the world node
|
|
return;
|
|
}
|
|
|
|
// Billboards always get the transform set.
|
|
if (egg_group->get_billboard_type() == EggGroup::BT_none) {
|
|
switch (_transform_type) {
|
|
case TT_all:
|
|
break;
|
|
|
|
case TT_model:
|
|
if (!egg_group->get_model_flag() && !egg_group->has_dcs_type()) {
|
|
return;
|
|
}
|
|
break;
|
|
|
|
case TT_dcs:
|
|
if (!egg_group->has_dcs_type()) {
|
|
return;
|
|
}
|
|
break;
|
|
|
|
case TT_none:
|
|
case TT_invalid:
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Extract the matrix from the dag path.
|
|
MMatrix mat = dag_path.inclusiveMatrix(&status);
|
|
if (!status) {
|
|
status.perror("Can't get transform matrix");
|
|
return;
|
|
}
|
|
LMatrix4d m4d(mat[0][0], mat[0][1], mat[0][2], mat[0][3],
|
|
mat[1][0], mat[1][1], mat[1][2], mat[1][3],
|
|
mat[2][0], mat[2][1], mat[2][2], mat[2][3],
|
|
mat[3][0], mat[3][1], mat[3][2], mat[3][3]);
|
|
|
|
// Maya has a rotate pivot, separate from its transform. Usually we
|
|
// care more about the rotate pivot than we do about the transform,
|
|
// so get the rotate pivot too.
|
|
MFnTransform transform(transformNode, &status);
|
|
if (!status) {
|
|
status.perror("MFnTransform constructor");
|
|
return;
|
|
}
|
|
MPoint pivot = transform.rotatePivot(MSpace::kObject, &status);
|
|
if (!status) {
|
|
status.perror("Can't get rotate pivot");
|
|
return;
|
|
}
|
|
|
|
// We need to convert the pivot to world coordinates. (Maya can
|
|
// only tell it to us in local coordinates.)
|
|
LPoint3d p3d(pivot[0], pivot[1], pivot[2]);
|
|
p3d = p3d * m4d;
|
|
|
|
// Now recenter the matrix about the pivot point.
|
|
m4d.set_row(3, p3d);
|
|
|
|
// Convert the recentered matrix into the group's space and store
|
|
// it.
|
|
m4d = m4d * egg_group->get_node_frame_inv();
|
|
if (!m4d.almost_equal(LMatrix4d::ident_mat(), 0.0001)) {
|
|
egg_group->add_matrix4(m4d);
|
|
}
|
|
return;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: MayaToEggConverter::get_joint_transform
|
|
// Access: Private
|
|
// Description: Extracts the transform on the indicated Maya node,
|
|
// as appropriate for a joint in an animated character,
|
|
// and applies it to the indicated node. This is
|
|
// different from get_transform() in that it does not
|
|
// respect the _transform_type flag, and it does not
|
|
// consider the relative transforms within the egg file.
|
|
////////////////////////////////////////////////////////////////////
|
|
void MayaToEggConverter::
|
|
get_joint_transform(const MDagPath &dag_path, EggGroup *egg_group) {
|
|
// First, make sure there's not a transform on the group already.
|
|
egg_group->clear_transform();
|
|
|
|
MStatus status;
|
|
MObject transformNode = dag_path.transform(&status);
|
|
// This node has no transform - i.e., it's the world node
|
|
if (!status && status.statusCode() == MStatus::kInvalidParameter) {
|
|
return;
|
|
}
|
|
|
|
MFnDagNode transform(transformNode, &status);
|
|
if (!status) {
|
|
status.perror("MFnDagNode constructor");
|
|
return;
|
|
}
|
|
|
|
MTransformationMatrix matrix(transform.transformationMatrix());
|
|
|
|
if (mayaegg_cat.is_spam()) {
|
|
MVector t = matrix.translation(MSpace::kWorld);
|
|
mayaegg_cat.spam()
|
|
<< " translation: ["
|
|
<< t[0] << ", "
|
|
<< t[1] << ", "
|
|
<< t[2] << "]\n";
|
|
double d[3];
|
|
MTransformationMatrix::RotationOrder rOrder;
|
|
|
|
matrix.getRotation(d, rOrder, MSpace::kWorld);
|
|
mayaegg_cat.spam()
|
|
<< " rotation: ["
|
|
<< d[0] << ", "
|
|
<< d[1] << ", "
|
|
<< d[2] << "]\n";
|
|
matrix.getScale(d, MSpace::kWorld);
|
|
mayaegg_cat.spam()
|
|
<< " scale: ["
|
|
<< d[0] << ", "
|
|
<< d[1] << ", "
|
|
<< d[2] << "]\n";
|
|
matrix.getShear(d, MSpace::kWorld);
|
|
mayaegg_cat.spam()
|
|
<< " shear: ["
|
|
<< d[0] << ", "
|
|
<< d[1] << ", "
|
|
<< d[2] << "]\n";
|
|
}
|
|
|
|
MMatrix mat = matrix.asMatrix();
|
|
MMatrix ident_mat;
|
|
ident_mat.setToIdentity();
|
|
|
|
if (!mat.isEquivalent(ident_mat, 0.0001)) {
|
|
egg_group->set_transform3d
|
|
(LMatrix4d(mat[0][0], mat[0][1], mat[0][2], mat[0][3],
|
|
mat[1][0], mat[1][1], mat[1][2], mat[1][3],
|
|
mat[2][0], mat[2][1], mat[2][2], mat[2][3],
|
|
mat[3][0], mat[3][1], mat[3][2], mat[3][3]));
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: MayaToEggConverter::make_nurbs_surface
|
|
// Access: Private
|
|
// Description: Converts the indicated Maya NURBS surface to a
|
|
// corresponding egg structure, and attaches it to the
|
|
// indicated egg group.
|
|
////////////////////////////////////////////////////////////////////
|
|
void MayaToEggConverter::
|
|
make_nurbs_surface(MayaNodeDesc *node_desc, const MDagPath &dag_path,
|
|
MFnNurbsSurface &surface, EggGroup *egg_group) {
|
|
MStatus status;
|
|
string name = surface.name().asChar();
|
|
|
|
if (mayaegg_cat.is_spam()) {
|
|
mayaegg_cat.spam()
|
|
<< " numCVs: "
|
|
<< surface.numCVsInU()
|
|
<< " * "
|
|
<< surface.numCVsInV()
|
|
<< "\n";
|
|
mayaegg_cat.spam()
|
|
<< " numKnots: "
|
|
<< surface.numKnotsInU()
|
|
<< " * "
|
|
<< surface.numKnotsInV()
|
|
<< "\n";
|
|
mayaegg_cat.spam()
|
|
<< " numSpans: "
|
|
<< surface.numSpansInU()
|
|
<< " * "
|
|
<< surface.numSpansInV()
|
|
<< "\n";
|
|
}
|
|
|
|
MayaShader *shader = _shaders.find_shader_for_node(surface.object());
|
|
|
|
if (_polygon_output) {
|
|
// If we want polygon output only, tesselate the NURBS and output
|
|
// that.
|
|
MTesselationParams params;
|
|
params.setFormatType(MTesselationParams::kStandardFitFormat);
|
|
params.setOutputType(MTesselationParams::kQuads);
|
|
params.setStdFractionalTolerance(_polygon_tolerance);
|
|
|
|
// We'll create the tesselation as a sibling of the NURBS surface.
|
|
// That way we inherit all of the transformations.
|
|
MDagPath polyset_path = dag_path;
|
|
MObject polyset_parent = polyset_path.node();
|
|
MObject polyset =
|
|
surface.tesselate(params, polyset_parent, &status);
|
|
if (!status) {
|
|
status.perror("MFnNurbsSurface::tesselate");
|
|
return;
|
|
}
|
|
|
|
status = polyset_path.push(polyset);
|
|
if (!status) {
|
|
status.perror("MDagPath::push");
|
|
}
|
|
|
|
MFnMesh polyset_fn(polyset, &status);
|
|
if (!status) {
|
|
status.perror("MFnMesh constructor");
|
|
return;
|
|
}
|
|
make_polyset(node_desc, polyset_path, polyset_fn, egg_group, shader);
|
|
|
|
// Now remove the polyset we created.
|
|
MFnDagNode parent_node(polyset_parent, &status);
|
|
if (!status) {
|
|
status.perror("MFnDagNode constructor");
|
|
return;
|
|
}
|
|
status = parent_node.removeChild(polyset);
|
|
if (!status) {
|
|
status.perror("MFnDagNode::removeChild");
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
MPointArray cv_array;
|
|
status = surface.getCVs(cv_array, MSpace::kWorld);
|
|
if (!status) {
|
|
status.perror("MFnNurbsSurface::getCVs");
|
|
return;
|
|
}
|
|
|
|
// Also get out all the alternate blend shapes for the surface by
|
|
// applying each morph slider one at a time.
|
|
pvector<MPointArray> morph_cvs;
|
|
if (_animation_convert == AC_model) {
|
|
int num_sliders = node_desc->get_num_blend_descs();
|
|
morph_cvs.reserve(num_sliders);
|
|
for (int i = 0; i < num_sliders; i++) {
|
|
MayaBlendDesc *blend_desc = node_desc->get_blend_desc(i);
|
|
|
|
// Temporarily push the slider up to 1.0 so we can see what the
|
|
// surface looks like at that value.
|
|
blend_desc->set_slider(1.0);
|
|
MPointArray cv_array;
|
|
status = surface.getCVs(cv_array, MSpace::kWorld);
|
|
blend_desc->set_slider(0.0);
|
|
|
|
if (!status) {
|
|
status.perror("MFnNurbsSurface::getCVs");
|
|
return;
|
|
}
|
|
morph_cvs.push_back(cv_array);
|
|
}
|
|
}
|
|
|
|
MDoubleArray u_knot_array, v_knot_array;
|
|
status = surface.getKnotsInU(u_knot_array);
|
|
if (!status) {
|
|
status.perror("MFnNurbsSurface::getKnotsInU");
|
|
return;
|
|
}
|
|
status = surface.getKnotsInV(v_knot_array);
|
|
if (!status) {
|
|
status.perror("MFnNurbsSurface::getKnotsInV");
|
|
return;
|
|
}
|
|
|
|
MFnNurbsSurface::Form u_form = surface.formInU();
|
|
MFnNurbsSurface::Form v_form = surface.formInV();
|
|
|
|
int u_degree = surface.degreeU();
|
|
int v_degree = surface.degreeV();
|
|
|
|
int u_cvs = surface.numCVsInU();
|
|
int v_cvs = surface.numCVsInV();
|
|
|
|
// Maya repeats CVS at the end for a periodic surface, and doesn't
|
|
// count them in the joint weight array, below.
|
|
int maya_u_cvs = (u_form == MFnNurbsSurface::kPeriodic) ? u_cvs - u_degree : u_cvs;
|
|
int maya_v_cvs = (v_form == MFnNurbsSurface::kPeriodic) ? v_cvs - v_degree : v_cvs;
|
|
|
|
int u_knots = surface.numKnotsInU();
|
|
int v_knots = surface.numKnotsInV();
|
|
|
|
assert(u_knots == u_cvs + u_degree - 1);
|
|
assert(v_knots == v_cvs + v_degree - 1);
|
|
|
|
string vpool_name = name + ".cvs";
|
|
EggVertexPool *vpool = new EggVertexPool(vpool_name);
|
|
egg_group->add_child(vpool);
|
|
|
|
EggNurbsSurface *egg_nurbs = new EggNurbsSurface(name);
|
|
egg_nurbs->setup(u_degree + 1, v_degree + 1,
|
|
u_knots + 2, v_knots + 2);
|
|
|
|
int i;
|
|
|
|
egg_nurbs->set_u_knot(0, u_knot_array[0]);
|
|
for (i = 0; i < u_knots; i++) {
|
|
egg_nurbs->set_u_knot(i + 1, u_knot_array[i]);
|
|
}
|
|
egg_nurbs->set_u_knot(u_knots + 1, u_knot_array[u_knots - 1]);
|
|
|
|
egg_nurbs->set_v_knot(0, v_knot_array[0]);
|
|
for (i = 0; i < v_knots; i++) {
|
|
egg_nurbs->set_v_knot(i + 1, v_knot_array[i]);
|
|
}
|
|
egg_nurbs->set_v_knot(v_knots + 1, v_knot_array[v_knots - 1]);
|
|
|
|
LMatrix4d vertex_frame_inv = egg_group->get_vertex_frame_inv();
|
|
|
|
for (i = 0; i < egg_nurbs->get_num_cvs(); i++) {
|
|
int ui = egg_nurbs->get_u_index(i);
|
|
int vi = egg_nurbs->get_v_index(i);
|
|
int maya_vi = v_cvs * ui + vi;
|
|
|
|
double v[4];
|
|
status = cv_array[maya_vi].get(v);
|
|
if (!status) {
|
|
status.perror("MPoint::get");
|
|
} else {
|
|
EggVertex *vert = vpool->add_vertex(new EggVertex, i);
|
|
LPoint4d p4d(v[0], v[1], v[2], v[3]);
|
|
p4d = p4d * vertex_frame_inv;
|
|
vert->set_pos(p4d);
|
|
|
|
// Now generate the morph targets for the vertex.
|
|
if (!morph_cvs.empty()) {
|
|
// Morph deltas are given in 3-d space, not in 4-d homogenous
|
|
// space.
|
|
LPoint3d p3d(v[0] / v[3], v[1] / v[3], v[2] / v[3]);
|
|
|
|
for (unsigned int si = 0; si < morph_cvs.size(); si++) {
|
|
MayaBlendDesc *blend_desc = node_desc->get_blend_desc(si);
|
|
status = morph_cvs[si][maya_vi].get(v);
|
|
if (!status) {
|
|
status.perror("MPoint::get");
|
|
} else {
|
|
LPoint3d m3d(v[0] / v[3], v[1] / v[3], v[2] / v[3]);
|
|
LVector3d delta = m3d - p3d;
|
|
if (!delta.almost_equal(LVector3d::zero())) {
|
|
EggMorphVertex dxyz(blend_desc->get_name(), delta);
|
|
vert->_dxyzs.insert(dxyz);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
egg_nurbs->add_vertex(vert);
|
|
}
|
|
}
|
|
|
|
// Now consider the trim curves, if any.
|
|
unsigned num_trims = surface.numRegions();
|
|
int trim_curve_index = 0;
|
|
for (unsigned ti = 0; ti < num_trims; ti++) {
|
|
unsigned num_loops = surface.numBoundaries(ti);
|
|
|
|
if (num_loops > 0) {
|
|
egg_nurbs->_trims.push_back(EggNurbsSurface::Trim());
|
|
EggNurbsSurface::Trim &egg_trim = egg_nurbs->_trims.back();
|
|
|
|
for (unsigned li = 0; li < num_loops; li++) {
|
|
egg_trim.push_back(EggNurbsSurface::Loop());
|
|
EggNurbsSurface::Loop &egg_loop = egg_trim.back();
|
|
|
|
MFnNurbsSurface::BoundaryType type =
|
|
surface.boundaryType(ti, li, &status);
|
|
bool keep_loop = false;
|
|
|
|
if (!status) {
|
|
status.perror("MFnNurbsSurface::BoundaryType");
|
|
} else {
|
|
keep_loop = (type == MFnNurbsSurface::kInner ||
|
|
type == MFnNurbsSurface::kOuter);
|
|
}
|
|
|
|
if (keep_loop) {
|
|
unsigned num_edges = surface.numEdges(ti, li);
|
|
for (unsigned ei = 0; ei < num_edges; ei++) {
|
|
MObjectArray edge = surface.edge(ti, li, ei, true, &status);
|
|
if (!status) {
|
|
status.perror("MFnNurbsSurface::edge");
|
|
} else {
|
|
unsigned num_segs = edge.length();
|
|
for (unsigned si = 0; si < num_segs; si++) {
|
|
MObject segment = edge[si];
|
|
if (segment.hasFn(MFn::kNurbsCurve)) {
|
|
MFnNurbsCurve curve(segment, &status);
|
|
if (!status) {
|
|
mayaegg_cat.error()
|
|
<< "Trim curve appears to be a nurbs curve, but isn't.\n";
|
|
} else {
|
|
// Finally, we have a valid curve!
|
|
EggNurbsCurve *egg_curve =
|
|
make_trim_curve(curve, name, egg_group, trim_curve_index);
|
|
trim_curve_index++;
|
|
if (egg_curve != (EggNurbsCurve *)NULL) {
|
|
egg_loop.push_back(egg_curve);
|
|
}
|
|
}
|
|
} else {
|
|
mayaegg_cat.error()
|
|
<< "Trim curve segment is not a nurbs curve.\n";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// We add the NURBS to the group down here, after all of the vpools
|
|
// for the trim curves have been added.
|
|
egg_group->add_child(egg_nurbs);
|
|
|
|
if (shader != (MayaShader *)NULL) {
|
|
set_shader_attributes(*egg_nurbs, *shader);
|
|
}
|
|
|
|
// Now try to find the skinning information for the surface.
|
|
bool got_weights = false;
|
|
|
|
pvector<EggGroup *> joints;
|
|
MFloatArray weights;
|
|
if (_animation_convert == AC_model) {
|
|
got_weights =
|
|
get_vertex_weights(dag_path, surface, joints, weights);
|
|
}
|
|
|
|
if (got_weights && !joints.empty()) {
|
|
int num_joints = joints.size();
|
|
int num_weights = (int)weights.length();
|
|
int num_verts = num_weights / num_joints;
|
|
// The number of weights should be an even multiple of verts *
|
|
// joints.
|
|
nassertv(num_weights == num_verts * num_joints);
|
|
|
|
for (i = 0; i < egg_nurbs->get_num_cvs(); i++) {
|
|
int ui = egg_nurbs->get_u_index(i) % maya_u_cvs;
|
|
int vi = egg_nurbs->get_v_index(i) % maya_v_cvs;
|
|
|
|
int maya_vi = maya_v_cvs * ui + vi;
|
|
nassertv(maya_vi < num_verts);
|
|
EggVertex *vert = vpool->get_vertex(i);
|
|
|
|
for (int ji = 0; ji < num_joints; ++ji) {
|
|
float weight = weights[maya_vi * num_joints + ji];
|
|
if (weight != 0.0f) {
|
|
EggGroup *joint = joints[ji];
|
|
if (joint != (EggGroup *)NULL) {
|
|
joint->ref_vertex(vert, weight);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: MayaToEggConverter::make_trim_curve
|
|
// Access: Private
|
|
// Description: Converts the indicated Maya NURBS trim curve to a
|
|
// corresponding egg structure, and returns it, or NULL
|
|
// if there is a problem.
|
|
////////////////////////////////////////////////////////////////////
|
|
EggNurbsCurve *MayaToEggConverter::
|
|
make_trim_curve(const MFnNurbsCurve &curve, const string &nurbs_name,
|
|
EggGroupNode *egg_group, int trim_curve_index) {
|
|
if (mayaegg_cat.is_spam()) {
|
|
mayaegg_cat.spam()
|
|
<< "Trim curve:\n";
|
|
mayaegg_cat.spam()
|
|
<< " numCVs: "
|
|
<< curve.numCVs()
|
|
<< "\n";
|
|
mayaegg_cat.spam()
|
|
<< " numKnots: "
|
|
<< curve.numKnots()
|
|
<< "\n";
|
|
mayaegg_cat.spam()
|
|
<< " numSpans: "
|
|
<< curve.numSpans()
|
|
<< "\n";
|
|
}
|
|
|
|
MStatus status;
|
|
|
|
MPointArray cv_array;
|
|
status = curve.getCVs(cv_array, MSpace::kWorld);
|
|
if (!status) {
|
|
status.perror("MFnNurbsCurve::getCVs");
|
|
return (EggNurbsCurve *)NULL;
|
|
}
|
|
MDoubleArray knot_array;
|
|
status = curve.getKnots(knot_array);
|
|
if (!status) {
|
|
status.perror("MFnNurbsCurve::getKnots");
|
|
return (EggNurbsCurve *)NULL;
|
|
}
|
|
|
|
/*
|
|
MFnNurbsCurve::Form form = curve.form();
|
|
*/
|
|
|
|
int degree = curve.degree();
|
|
int cvs = curve.numCVs();
|
|
int knots = curve.numKnots();
|
|
|
|
assert(knots == cvs + degree - 1);
|
|
|
|
string trim_name = "trim" + format_string(trim_curve_index);
|
|
|
|
string vpool_name = nurbs_name + "." + trim_name;
|
|
EggVertexPool *vpool = new EggVertexPool(vpool_name);
|
|
egg_group->add_child(vpool);
|
|
|
|
EggNurbsCurve *egg_curve = new EggNurbsCurve(trim_name);
|
|
egg_curve->setup(degree + 1, knots + 2);
|
|
|
|
int i;
|
|
|
|
egg_curve->set_knot(0, knot_array[0]);
|
|
for (i = 0; i < knots; i++) {
|
|
egg_curve->set_knot(i + 1, knot_array[i]);
|
|
}
|
|
egg_curve->set_knot(knots + 1, knot_array[knots - 1]);
|
|
|
|
for (i = 0; i < egg_curve->get_num_cvs(); i++) {
|
|
double v[4];
|
|
MStatus status = cv_array[i].get(v);
|
|
if (!status) {
|
|
status.perror("MPoint::get");
|
|
} else {
|
|
EggVertex vert;
|
|
vert.set_pos(LPoint3d(v[0], v[1], v[3]));
|
|
egg_curve->add_vertex(vpool->create_unique_vertex(vert));
|
|
}
|
|
}
|
|
|
|
return egg_curve;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: MayaToEggConverter::make_nurbs_curve
|
|
// Access: Private
|
|
// Description: Converts the indicated Maya NURBS curve (a standalone
|
|
// curve, not a trim curve) to a corresponding egg
|
|
// structure and attaches it to the indicated egg group.
|
|
////////////////////////////////////////////////////////////////////
|
|
void MayaToEggConverter::
|
|
make_nurbs_curve(const MDagPath &, const MFnNurbsCurve &curve,
|
|
EggGroup *egg_group) {
|
|
MStatus status;
|
|
string name = curve.name().asChar();
|
|
|
|
if (mayaegg_cat.is_spam()) {
|
|
mayaegg_cat.spam()
|
|
<< " numCVs: "
|
|
<< curve.numCVs()
|
|
<< "\n";
|
|
mayaegg_cat.spam()
|
|
<< " numKnots: "
|
|
<< curve.numKnots()
|
|
<< "\n";
|
|
mayaegg_cat.spam()
|
|
<< " numSpans: "
|
|
<< curve.numSpans()
|
|
<< "\n";
|
|
}
|
|
|
|
MPointArray cv_array;
|
|
status = curve.getCVs(cv_array, MSpace::kWorld);
|
|
if (!status) {
|
|
status.perror("MFnNurbsCurve::getCVs");
|
|
return;
|
|
}
|
|
MDoubleArray knot_array;
|
|
status = curve.getKnots(knot_array);
|
|
if (!status) {
|
|
status.perror("MFnNurbsCurve::getKnots");
|
|
return;
|
|
}
|
|
|
|
/*
|
|
MFnNurbsCurve::Form form = curve.form();
|
|
*/
|
|
|
|
int degree = curve.degree();
|
|
int cvs = curve.numCVs();
|
|
int knots = curve.numKnots();
|
|
|
|
assert(knots == cvs + degree - 1);
|
|
|
|
string vpool_name = name + ".cvs";
|
|
EggVertexPool *vpool = new EggVertexPool(vpool_name);
|
|
egg_group->add_child(vpool);
|
|
|
|
EggNurbsCurve *egg_curve = new EggNurbsCurve(name);
|
|
egg_group->add_child(egg_curve);
|
|
egg_curve->setup(degree + 1, knots + 2);
|
|
|
|
int i;
|
|
|
|
egg_curve->set_knot(0, knot_array[0]);
|
|
for (i = 0; i < knots; i++) {
|
|
egg_curve->set_knot(i + 1, knot_array[i]);
|
|
}
|
|
egg_curve->set_knot(knots + 1, knot_array[knots - 1]);
|
|
|
|
LMatrix4d vertex_frame_inv = egg_group->get_vertex_frame_inv();
|
|
|
|
for (i = 0; i < egg_curve->get_num_cvs(); i++) {
|
|
double v[4];
|
|
MStatus status = cv_array[i].get(v);
|
|
if (!status) {
|
|
status.perror("MPoint::get");
|
|
} else {
|
|
EggVertex vert;
|
|
LPoint4d p4d(v[0], v[1], v[2], v[3]);
|
|
p4d = p4d * vertex_frame_inv;
|
|
vert.set_pos(p4d);
|
|
egg_curve->add_vertex(vpool->create_unique_vertex(vert));
|
|
}
|
|
}
|
|
|
|
MayaShader *shader = _shaders.find_shader_for_node(curve.object());
|
|
if (shader != (MayaShader *)NULL) {
|
|
set_shader_attributes(*egg_curve, *shader);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: MayaShader::round uvs
|
|
// Access: Private
|
|
// Description: given uvsets, round them up or down
|
|
////////////////////////////////////////////////////////////////////
|
|
int MayaToEggConverter::
|
|
round(double value) {
|
|
if (value < 0)
|
|
return -(floor(-value + 0.5));
|
|
// or as an alternate use:
|
|
// return ceil ( value - 0.5);
|
|
else
|
|
return floor( value + 0.5);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: MayaToEggConverter::make_polyset
|
|
// Access: Private
|
|
// Description: Converts the indicated Maya polyset to a bunch of
|
|
// EggPolygons and parents them to the indicated egg
|
|
// group.
|
|
////////////////////////////////////////////////////////////////////
|
|
void MayaToEggConverter::
|
|
make_polyset(MayaNodeDesc *node_desc, const MDagPath &dag_path,
|
|
const MFnMesh &mesh, EggGroup *egg_group,
|
|
MayaShader *default_shader) {
|
|
MStatus status;
|
|
string name = mesh.name().asChar();
|
|
|
|
MObject mesh_object = mesh.object();
|
|
bool maya_double_sided = false;
|
|
get_bool_attribute(mesh_object, "doubleSided", maya_double_sided);
|
|
|
|
if (mayaegg_cat.is_spam()) {
|
|
mayaegg_cat.spam()
|
|
<< " numPolygons: "
|
|
<< mesh.numPolygons()
|
|
<< "\n";
|
|
mayaegg_cat.spam()
|
|
<< " numVertices: "
|
|
<< mesh.numVertices()
|
|
<< "\n";
|
|
}
|
|
|
|
if (mesh.numPolygons() == 0) {
|
|
if (mayaegg_cat.is_debug()) {
|
|
mayaegg_cat.debug()
|
|
<< "Ignoring empty mesh " << name << "\n";
|
|
}
|
|
return;
|
|
}
|
|
|
|
string vpool_name = name + ".verts";
|
|
EggVertexPool *vpool = new EggVertexPool(vpool_name);
|
|
egg_group->add_child(vpool);
|
|
|
|
// One way to convert the mesh would be to first get out all the
|
|
// vertices in the mesh and add them into the vpool, then when we
|
|
// traverse the polygons we would only have to index them into the
|
|
// vpool according to their Maya vertex index.
|
|
|
|
// Unfortunately, since Maya may store multiple normals and/or
|
|
// colors for each vertex according to which polygon it is in, that
|
|
// approach won't necessarily work. In egg, those split-property
|
|
// vertices have to become separate vertices. So instead of adding
|
|
// all the vertices up front, we'll start with an empty vpool, and
|
|
// add vertices to it on the fly.
|
|
|
|
MObject component_obj;
|
|
MItMeshPolygon pi(dag_path, component_obj, &status);
|
|
if (!status) {
|
|
status.perror("MItMeshPolygon constructor");
|
|
return;
|
|
}
|
|
|
|
MObjectArray shaders;
|
|
MIntArray poly_shader_indices;
|
|
|
|
status = mesh.getConnectedShaders(dag_path.instanceNumber(),
|
|
shaders, poly_shader_indices);
|
|
if (!status) {
|
|
status.perror("MFnMesh::getConnectedShaders");
|
|
}
|
|
|
|
// We will need to transform all vertices from world coordinate
|
|
// space into the vertex space appropriate to this node. Usually,
|
|
// this is the same thing as world coordinate space, and this matrix
|
|
// will be identity; but if the node is under an instance
|
|
// (particularly, for instance, a billboard) then the vertex space
|
|
// will be different from world space.
|
|
LMatrix4d vertex_frame_inv = egg_group->get_vertex_frame_inv();
|
|
|
|
// Save these modeling flags for the check below.
|
|
bool egg_vertex_color = false;
|
|
bool egg_double_sided = false;
|
|
if (egg_group->has_user_data(MayaEggGroupUserData::get_class_type())) {
|
|
MayaEggGroupUserData *user_data =
|
|
DCAST(MayaEggGroupUserData, egg_group->get_user_data());
|
|
egg_vertex_color = user_data->_vertex_color;
|
|
egg_double_sided = user_data->_double_sided;
|
|
}
|
|
|
|
bool double_sided = maya_double_sided;
|
|
if (!_respect_maya_double_sided) {
|
|
// If this flag is false, we respect the maya double-sided
|
|
// settings only if the egg "double-sided" flag is also set.
|
|
if (!egg_double_sided) {
|
|
double_sided = false;
|
|
}
|
|
}
|
|
|
|
bool keep_all_uvsets = _keep_all_uvsets || node_desc->has_object_type("keep-all-uvsets");
|
|
if (node_desc->has_object_type("keep-all-uvsets")) {
|
|
mayaegg_cat.info() << "will keep_all_uvsets" << endl;
|
|
}
|
|
|
|
_shaders.bind_uvsets(mesh.object());
|
|
|
|
while (!pi.isDone()) {
|
|
EggPolygon *egg_poly = new EggPolygon;
|
|
egg_group->add_child(egg_poly);
|
|
|
|
egg_poly->set_bface_flag(double_sided);
|
|
|
|
// Determine the shader for this particular polygon.
|
|
MayaShader *shader = NULL;
|
|
int index = pi.index();
|
|
nassertv(index >= 0 && index < (int)poly_shader_indices.length());
|
|
int shader_index = poly_shader_indices[index];
|
|
|
|
if (shader_index != -1) {
|
|
nassertv(shader_index >= 0 && shader_index < (int)shaders.length());
|
|
MObject engine = shaders[shader_index];
|
|
shader =
|
|
_shaders.find_shader_for_shading_engine(engine);
|
|
|
|
} else if (default_shader != (MayaShader *)NULL) {
|
|
shader = default_shader;
|
|
}
|
|
|
|
const MayaShaderColorDef *default_color_def = NULL;
|
|
|
|
// And apply the shader properties to the polygon.
|
|
if (shader != (MayaShader *)NULL) {
|
|
set_shader_attributes(*egg_poly, *shader, true);
|
|
default_color_def = shader->get_color_def();
|
|
}
|
|
|
|
// Should we extract the color from the vertices? Normally, in
|
|
// Maya a texture completely replaces the vertex color, so we
|
|
// should ignore the vertex color if we have a texture.
|
|
|
|
// However, this is an inconvenient property of Maya; sometimes we
|
|
// really do want both vertex color and texture applied to the
|
|
// same object. To allow this, we define the special egg flag
|
|
// "vertex-color", which when set indicates that we should
|
|
// respect the vertex color anyway.
|
|
|
|
// Furthermore, if _always_show_vertex_color is true, we pretend
|
|
// that the "vertex-color" flag is always set.
|
|
bool ignore_vertex_color = false;
|
|
if ( default_color_def != (MayaShaderColorDef *)NULL) {
|
|
ignore_vertex_color = default_color_def->_has_texture && !(egg_vertex_color || _always_show_vertex_color);
|
|
}
|
|
|
|
Colorf poly_color(1.0f, 1.0f, 1.0f, 1.0f);
|
|
if (!ignore_vertex_color) {
|
|
// If we're respecting the vertex color, then remove the color
|
|
// specification from the polygon (so we can apply it to the
|
|
// vertices).
|
|
poly_color = egg_poly->get_color();
|
|
egg_poly->clear_color();
|
|
}
|
|
|
|
// Get the vertices for the polygon.
|
|
long num_verts = pi.polygonVertexCount();
|
|
long i;
|
|
LPoint3d centroid(0.0, 0.0, 0.0);
|
|
|
|
if (default_color_def != (MayaShaderColorDef *)NULL && default_color_def->has_projection()) {
|
|
// If the shader has a projection, we may need to compute the
|
|
// polygon's centroid to avoid seams at the edges.
|
|
for (i = 0; i < num_verts; i++) {
|
|
MPoint p = pi.point(i, MSpace::kWorld);
|
|
LPoint3d p3d(p[0], p[1], p[2]);
|
|
p3d = p3d * vertex_frame_inv;
|
|
centroid += p3d;
|
|
}
|
|
centroid /= (double)num_verts;
|
|
}
|
|
for (i = 0; i < num_verts; i++) {
|
|
EggVertex vert;
|
|
|
|
MPoint p = pi.point(i, MSpace::kWorld);
|
|
LPoint3d p3d(p[0] / p[3], p[1] / p[3], p[2] / p[3]);
|
|
p3d = p3d * vertex_frame_inv;
|
|
vert.set_pos(p3d);
|
|
|
|
MVector n;
|
|
status = pi.getNormal(i, n, MSpace::kWorld);
|
|
if (!status) {
|
|
status.perror("MItMeshPolygon::getNormal");
|
|
} else {
|
|
Normald n3d(n[0], n[1], n[2]);
|
|
n3d = n3d * vertex_frame_inv;
|
|
vert.set_normal(n3d);
|
|
}
|
|
|
|
// Go thru all the texture references for this primitive and set uvs
|
|
if (mayaegg_cat.is_debug()) {
|
|
if (shader != (MayaShader *)NULL) {
|
|
mayaegg_cat.debug() << "shader->_color.size is " << shader->_color.size() << endl;
|
|
}
|
|
mayaegg_cat.debug() << "primitive->tref.size is " << egg_poly->get_num_textures() << endl;
|
|
}
|
|
for (size_t ti=0; ti< _shaders._uvset_names.size(); ++ti) {
|
|
// get the eggTexture pointer
|
|
string uvset_name(_shaders._uvset_names[ti]);
|
|
string panda_uvset_name = uvset_name;
|
|
if (panda_uvset_name == "map1") {
|
|
panda_uvset_name = "default";
|
|
}
|
|
if (mayaegg_cat.is_debug()) {
|
|
mayaegg_cat.debug() << "--uvset_name :" << uvset_name << endl;
|
|
}
|
|
|
|
// get the shader color def that matches this EggTexture
|
|
// Asad: optimizing uvset: to discard unused uvsets. This for
|
|
// loop figures out which ones are unused.
|
|
|
|
bool keep_uv = keep_all_uvsets;
|
|
bool project_uv = false;
|
|
TexCoordd uv_projection;
|
|
|
|
if (shader != (MayaShader *)NULL) {
|
|
for (size_t tj = 0; tj < shader->_all_maps.size(); ++tj) {
|
|
MayaShaderColorDef *def = shader->_all_maps[tj];
|
|
if (def->_uvset_name == uvset_name) {
|
|
if (mayaegg_cat.is_debug()) {
|
|
mayaegg_cat.debug() << "matched colordef idx: " << tj << endl;
|
|
}
|
|
keep_uv = true;
|
|
if (def->has_projection()) {
|
|
project_uv = true;
|
|
uv_projection = def->project_uv(p3d, centroid);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// if uvset is not used don't add it to the vertex
|
|
if (!keep_uv) {
|
|
if (mayaegg_cat.is_spam()) {
|
|
mayaegg_cat.spam() << "discarding unused uvset " << uvset_name << endl;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (project_uv) {
|
|
// If the shader has a projection, use it instead of the
|
|
// polygon's built-in UV's.
|
|
vert.set_uv(panda_uvset_name, uv_projection);
|
|
} else {
|
|
// Get the UV's from the polygon.
|
|
float2 uvs;
|
|
MString uv_mstring(uvset_name.c_str());
|
|
if (pi.hasUVs(uv_mstring, &status)) {
|
|
status = pi.getUV(i, uvs, &uv_mstring);
|
|
if (!status) {
|
|
status.perror("MItMeshPolygon::getUV");
|
|
} else {
|
|
if (_round_uvs) {
|
|
if (uvs[0] > 1.0 || uvs[0] < -1.0) {
|
|
// apply upto 1/1000th precision, but round up
|
|
uvs[0] = (long)(uvs[0]*1000);
|
|
mayaegg_cat.debug() << "before rounding uvs[0]: " << uvs[0] << endl;
|
|
uvs[0] = (double)(round((double)uvs[0]/10.0)*10.0)/1000.0;
|
|
mayaegg_cat.debug() << "after rounding uvs[0]: " << uvs[0] << endl;
|
|
}
|
|
if (uvs[1] > 1.0 || uvs[1] < -1.0) {
|
|
uvs[1] = (long)(uvs[1]*1000);
|
|
mayaegg_cat.debug() << "before rounding uvs[1]: " << uvs[1] << endl;
|
|
uvs[1] = (double)(round((double)uvs[1]/10.0)*10.0)/1000.0;
|
|
mayaegg_cat.debug() << "after rounding uvs[1]: " << uvs[1] << endl;
|
|
}
|
|
}
|
|
vert.set_uv(panda_uvset_name, TexCoordd(uvs[0], uvs[1]));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!ignore_vertex_color) {
|
|
if (mayaegg_cat.is_spam()) {
|
|
mayaegg_cat.spam() << "poly_color = " << poly_color << endl;
|
|
}
|
|
if (pi.hasColor()) {
|
|
MColor c;
|
|
status = pi.getColor(c, i);
|
|
if (!status) {
|
|
status.perror("MItMeshPolygon::getColor");
|
|
} else {
|
|
// I saw instances where the color components exceeded 1.0
|
|
// so lets clamp the values to 0 to 1
|
|
c /= 1.0;
|
|
// The vertex color is a color scale that modifies the
|
|
// polygon color, not an override that replaces it.
|
|
vert.set_color(Colorf(c.r * poly_color[0], c.g * poly_color[1],
|
|
c.b * poly_color[2], c.a * poly_color[3]));
|
|
|
|
if (mayaegg_cat.is_spam()) {
|
|
mayaegg_cat.spam() << "maya_color = " << c << endl;
|
|
mayaegg_cat.spam() << "vert_color = " << vert.get_color() << endl;
|
|
}
|
|
}
|
|
} else {
|
|
vert.set_color(poly_color);
|
|
}
|
|
}
|
|
|
|
vert.set_external_index(pi.vertexIndex(i, &status));
|
|
|
|
egg_poly->add_vertex(vpool->create_unique_vertex(vert));
|
|
}
|
|
|
|
// Also get the face normal for the polygon.
|
|
Normald face_normal;
|
|
bool got_face_normal = false;
|
|
|
|
MVector n;
|
|
status = pi.getNormal(n, MSpace::kWorld);
|
|
if (!status) {
|
|
status.perror("MItMeshPolygon::getNormal face");
|
|
} else {
|
|
face_normal.set(n[0], n[1], n[2]);
|
|
face_normal = face_normal * vertex_frame_inv;
|
|
got_face_normal = true;
|
|
egg_poly->set_normal(face_normal);
|
|
}
|
|
|
|
// Now, check that the vertex ordering is consistent with the
|
|
// direction of the normals. If not, reverse the vertex ordering
|
|
// (since we have seen cases where Maya sets this in contradiction
|
|
// to its normals).
|
|
Normald order_normal;
|
|
if (got_face_normal && egg_poly->calculate_normal(order_normal)) {
|
|
if (order_normal.dot(face_normal) < 0.0) {
|
|
egg_poly->reverse_vertex_ordering();
|
|
if (mayaegg_cat.is_debug()) {
|
|
mayaegg_cat.debug()
|
|
<< "reversing polygon\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
pi.next();
|
|
}
|
|
if (mayaegg_cat.is_spam()) {
|
|
mayaegg_cat.spam() << "done traversing polys" << endl;
|
|
}
|
|
|
|
// Now that we've added all the polygons (and created all the
|
|
// vertices), go back through the vertex pool and set up the
|
|
// appropriate joint membership for each of the vertices.
|
|
bool got_weights = false;
|
|
|
|
pvector<EggGroup *> joints;
|
|
MFloatArray weights;
|
|
if (_animation_convert == AC_model) {
|
|
got_weights =
|
|
get_vertex_weights(dag_path, mesh, joints, weights);
|
|
}
|
|
|
|
if (got_weights && !joints.empty()) {
|
|
int num_joints = joints.size();
|
|
int num_weights = (int)weights.length();
|
|
int num_verts = num_weights / num_joints;
|
|
// The number of weights should be an even multiple of verts *
|
|
// joints.
|
|
nassertv(num_weights == num_verts * num_joints);
|
|
|
|
EggVertexPool::iterator vi;
|
|
for (vi = vpool->begin(); vi != vpool->end(); ++vi) {
|
|
EggVertex *vert = (*vi);
|
|
int maya_vi = vert->get_external_index();
|
|
nassertv(maya_vi >= 0 && maya_vi < num_verts);
|
|
|
|
for (int ji = 0; ji < num_joints; ++ji) {
|
|
float weight = weights[maya_vi * num_joints + ji];
|
|
if (weight != 0.0f) {
|
|
EggGroup *joint = joints[ji];
|
|
if (joint != (EggGroup *)NULL) {
|
|
joint->ref_vertex(vert, weight);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// We also need to compute the vertex morphs for the polyset, based
|
|
// on whatever blend shapes may be present. This is similar to the
|
|
// code in make_nurbs_surface(), except that since we don't have a
|
|
// one-to-one relationship of egg vertices to Maya vertices, we have
|
|
// to get the morphs down here, after we have added all of the egg
|
|
// vertices.
|
|
|
|
if (_animation_convert == AC_model) {
|
|
int num_orig_mesh_verts = mesh.numVertices();
|
|
|
|
int num_sliders = node_desc->get_num_blend_descs();
|
|
for (int i = 0; i < num_sliders; i++) {
|
|
MayaBlendDesc *blend_desc = node_desc->get_blend_desc(i);
|
|
|
|
// Temporarily push the slider up to 1.0 so we can see what the
|
|
// surface looks like at that value.
|
|
blend_desc->set_slider(1.0);
|
|
|
|
// We have to get the mesh object from the dag again after
|
|
// fiddling with the slider.
|
|
MFnMesh blend_mesh(dag_path, &status);
|
|
if (!status) {
|
|
mayaegg_cat.warning()
|
|
<< name << " no longer has a mesh after applying "
|
|
<< blend_desc->get_name() << "\n";
|
|
|
|
} else {
|
|
if (blend_mesh.numVertices() != num_orig_mesh_verts) {
|
|
mayaegg_cat.warning()
|
|
<< "Ignoring " << blend_desc->get_name() << " for "
|
|
<< name << "; blend shape has " << blend_mesh.numVertices()
|
|
<< " vertices while original shape has "
|
|
<< num_orig_mesh_verts << ".\n";
|
|
|
|
} else {
|
|
MPointArray verts;
|
|
status = blend_mesh.getPoints(verts, MSpace::kWorld);
|
|
if (!status) {
|
|
status.perror("MFnMesh::getPoints");
|
|
} else {
|
|
int num_verts = (int)verts.length();
|
|
EggVertexPool::iterator vi;
|
|
for (vi = vpool->begin(); vi != vpool->end(); ++vi) {
|
|
EggVertex *vert = (*vi);
|
|
int maya_vi = vert->get_external_index();
|
|
nassertv(maya_vi >= 0 && maya_vi < num_verts);
|
|
|
|
const MPoint &m = verts[maya_vi];
|
|
LPoint3d m3d(m[0] / m[3], m[1] / m[3], m[2] / m[3]);
|
|
m3d = m3d * vertex_frame_inv;
|
|
|
|
LVector3d delta = m3d - vert->get_pos3();
|
|
if (!delta.almost_equal(LVector3d::zero())) {
|
|
EggMorphVertex dxyz(blend_desc->get_name(), delta);
|
|
vert->_dxyzs.insert(dxyz);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
blend_desc->set_slider(0.0);
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: MayaToEggConverter::make_locator
|
|
// Access: Private
|
|
// Description: Locators are used in Maya to indicate a particular
|
|
// position in space to the user or the modeler. We
|
|
// represent that in egg with an ordinary Group node,
|
|
// which we transform by the locator's position, so that
|
|
// the indicated point becomes the origin at this node
|
|
// and below.
|
|
////////////////////////////////////////////////////////////////////
|
|
void MayaToEggConverter::
|
|
make_locator(const MDagPath &dag_path, const MFnDagNode &dag_node,
|
|
EggGroup *egg_group) {
|
|
MStatus status;
|
|
|
|
unsigned int num_children = dag_node.childCount();
|
|
MObject locator;
|
|
bool found_locator = false;
|
|
for (unsigned int ci = 0; ci < num_children && !found_locator; ci++) {
|
|
locator = dag_node.child(ci);
|
|
found_locator = (locator.apiType() == MFn::kLocator);
|
|
}
|
|
|
|
if (!found_locator) {
|
|
mayaegg_cat.error()
|
|
<< "Couldn't find locator within locator node "
|
|
<< dag_path.fullPathName().asChar() << "\n";
|
|
return;
|
|
}
|
|
|
|
LPoint3d p3d;
|
|
if (!get_vec3d_attribute(locator, "localPosition", p3d)) {
|
|
mayaegg_cat.error()
|
|
<< "Couldn't get position of locator "
|
|
<< dag_path.fullPathName().asChar() << "\n";
|
|
return;
|
|
}
|
|
|
|
// We need to convert the position to world coordinates. For some
|
|
// reason, Maya can only tell it to us in local coordinates.
|
|
MMatrix mat = dag_path.inclusiveMatrix(&status);
|
|
if (!status) {
|
|
status.perror("Can't get coordinate space for locator");
|
|
return;
|
|
}
|
|
LMatrix4d n2w(mat[0][0], mat[0][1], mat[0][2], mat[0][3],
|
|
mat[1][0], mat[1][1], mat[1][2], mat[1][3],
|
|
mat[2][0], mat[2][1], mat[2][2], mat[2][3],
|
|
mat[3][0], mat[3][1], mat[3][2], mat[3][3]);
|
|
p3d = p3d * n2w;
|
|
|
|
// Now convert the locator point into the group's space.
|
|
p3d = p3d * egg_group->get_node_frame_inv();
|
|
|
|
egg_group->add_translate3d(p3d);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: MayaToEggConverter::get_vertex_weights
|
|
// Access: Private
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
bool MayaToEggConverter::
|
|
get_vertex_weights(const MDagPath &dag_path, const MFnMesh &mesh,
|
|
pvector<EggGroup *> &joints, MFloatArray &weights) {
|
|
MStatus status;
|
|
|
|
// Since we are working with a mesh the input attribute that
|
|
// creates the mesh is named "inMesh"
|
|
//
|
|
MObject attr = mesh.attribute("inMesh");
|
|
|
|
// Create the plug to the "inMesh" attribute then use the
|
|
// DG iterator to walk through the DG, at the node level.
|
|
//
|
|
MPlug history(mesh.object(), attr);
|
|
MItDependencyGraph it(history, MFn::kDependencyNode,
|
|
MItDependencyGraph::kUpstream,
|
|
MItDependencyGraph::kDepthFirst,
|
|
MItDependencyGraph::kNodeLevel);
|
|
|
|
while (!it.isDone()) {
|
|
// We will walk along the node level of the DG until we
|
|
// spot a skinCluster node.
|
|
//
|
|
MObject c_node = it.thisNode();
|
|
if (c_node.hasFn(MFn::kSkinClusterFilter)) {
|
|
// We've found the cluster handle. Try to get the weight
|
|
// data.
|
|
//
|
|
MFnSkinCluster cluster(c_node, &status);
|
|
if (!status) {
|
|
status.perror("MFnSkinCluster constructor");
|
|
return false;
|
|
}
|
|
|
|
// Get the set of objects that influence the vertices of this
|
|
// mesh. Hopefully these will all be joints.
|
|
MDagPathArray influence_objects;
|
|
cluster.influenceObjects(influence_objects, &status);
|
|
if (!status) {
|
|
status.perror("MFnSkinCluster::influenceObjects");
|
|
|
|
} else {
|
|
// Fill up the vector with the corresponding table of egg
|
|
// groups for each joint.
|
|
joints.clear();
|
|
for (unsigned oi = 0; oi < influence_objects.length(); oi++) {
|
|
MDagPath joint_dag_path = influence_objects[oi];
|
|
MayaNodeDesc *joint_node_desc = _tree.build_node(joint_dag_path);
|
|
EggGroup *joint = _tree.get_egg_group(joint_node_desc);
|
|
joints.push_back(joint);
|
|
}
|
|
|
|
// Now use a component object to retrieve all of the weight
|
|
// data in one API call.
|
|
MFnSingleIndexedComponent sic;
|
|
MObject sic_object = sic.create(MFn::kMeshVertComponent);
|
|
sic.setCompleteData(mesh.numVertices());
|
|
unsigned influence_count;
|
|
|
|
status = cluster.getWeights(dag_path, sic_object,
|
|
weights, influence_count);
|
|
if (!status) {
|
|
status.perror("MFnSkinCluster::getWeights");
|
|
} else {
|
|
if (influence_count != influence_objects.length()) {
|
|
mayaegg_cat.error()
|
|
<< "MFnSkinCluster::influenceObjects() returns "
|
|
<< influence_objects.length()
|
|
<< " objects, but MFnSkinCluster::getWeights() reports "
|
|
<< influence_count << " objects.\n";
|
|
|
|
} else {
|
|
// We've got the weights and the set of objects. That's all
|
|
// we need.
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
it.next();
|
|
}
|
|
|
|
// The mesh was not soft-skinned.
|
|
return false;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: MayaToEggConverter::get_vertex_weights
|
|
// Access: Private
|
|
// Description: As above, for a NURBS surface instead of a polygon
|
|
// mesh.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool MayaToEggConverter::
|
|
get_vertex_weights(const MDagPath &dag_path, const MFnNurbsSurface &surface,
|
|
pvector<EggGroup *> &joints, MFloatArray &weights) {
|
|
MStatus status;
|
|
|
|
// Since we are working with a NURBS surface the input attribute that
|
|
// creates the surface is named "create"
|
|
//
|
|
MObject attr = surface.attribute("create");
|
|
|
|
// Create the plug to the "create" attribute then use the
|
|
// DG iterator to walk through the DG, at the node level.
|
|
//
|
|
MPlug history(surface.object(), attr);
|
|
MItDependencyGraph it(history, MFn::kDependencyNode,
|
|
MItDependencyGraph::kUpstream,
|
|
MItDependencyGraph::kDepthFirst,
|
|
MItDependencyGraph::kNodeLevel);
|
|
|
|
while (!it.isDone()) {
|
|
// We will walk along the node level of the DG until we
|
|
// spot a skinCluster node.
|
|
//
|
|
MObject c_node = it.thisNode();
|
|
if (c_node.hasFn(MFn::kSkinClusterFilter)) {
|
|
// We've found the cluster handle. Try to get the weight
|
|
// data.
|
|
//
|
|
MFnSkinCluster cluster(c_node, &status);
|
|
if (!status) {
|
|
status.perror("MFnSkinCluster constructor");
|
|
return false;
|
|
}
|
|
|
|
// Get the set of objects that influence the vertices of this
|
|
// surface. Hopefully these will all be joints.
|
|
MDagPathArray influence_objects;
|
|
cluster.influenceObjects(influence_objects, &status);
|
|
if (!status) {
|
|
status.perror("MFnSkinCluster::influenceObjects");
|
|
|
|
} else {
|
|
// Fill up the vector with the corresponding table of egg
|
|
// groups for each joint.
|
|
joints.clear();
|
|
for (unsigned oi = 0; oi < influence_objects.length(); oi++) {
|
|
MDagPath joint_dag_path = influence_objects[oi];
|
|
MayaNodeDesc *joint_node_desc = _tree.build_node(joint_dag_path);
|
|
EggGroup *joint = _tree.get_egg_group(joint_node_desc);
|
|
joints.push_back(joint);
|
|
}
|
|
|
|
// Now use a component object to retrieve all of the weight
|
|
// data in one API call.
|
|
MFnDoubleIndexedComponent dic;
|
|
MObject dic_object = dic.create(MFn::kSurfaceCVComponent);
|
|
dic.setCompleteData(surface.numCVsInU(), surface.numCVsInV());
|
|
unsigned influence_count;
|
|
|
|
status = cluster.getWeights(dag_path, dic_object,
|
|
weights, influence_count);
|
|
if (!status) {
|
|
status.perror("MFnSkinCluster::getWeights");
|
|
} else {
|
|
if (influence_count != influence_objects.length()) {
|
|
mayaegg_cat.error()
|
|
<< "MFnSkinCluster::influenceObjects() returns "
|
|
<< influence_objects.length()
|
|
<< " objects, but MFnSkinCluster::getWeights() reports "
|
|
<< influence_count << " objects.\n";
|
|
|
|
} else {
|
|
// We've got the weights and the set of objects. That's all
|
|
// we need.
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
it.next();
|
|
}
|
|
|
|
// The surface was not soft-skinned.
|
|
return false;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: MayaShader::set_shader_attributes
|
|
// Access: Private
|
|
// Description: Applies the known shader attributes to the indicated
|
|
// egg primitive. Note: For multi-textures, Maya lists
|
|
// the top most texture in slot 0. But Panda puts the
|
|
// base texture at slot 0. Hence I parse the list of
|
|
// textures from last to first.
|
|
////////////////////////////////////////////////////////////////////
|
|
void MayaToEggConverter::
|
|
set_shader_attributes(EggPrimitive &primitive, const MayaShader &shader,
|
|
bool mesh) {
|
|
if (shader._legacy_mode) {
|
|
set_shader_legacy(primitive, shader, mesh);
|
|
} else {
|
|
set_shader_modern(primitive, shader, mesh);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: MayaToEggConverter::set_shader_modern
|
|
// Access: Private
|
|
// Description: The modern implementation of set_shader_attributes.
|
|
//
|
|
// In the modern codepath, the MayaShader is a direct,
|
|
// literal representation of a list of EggTextures.
|
|
// All this exporter has to do is translate the list
|
|
// without interpretation. All the complex interpretation
|
|
// is handled elsewhere, in the MayaShader module.
|
|
////////////////////////////////////////////////////////////////////
|
|
void MayaToEggConverter::
|
|
set_shader_modern(EggPrimitive &primitive, const MayaShader &shader,
|
|
bool mesh) {
|
|
|
|
for (size_t idx=0; idx < shader._all_maps.size(); idx++) {
|
|
MayaShaderColorDef *def = shader._all_maps[idx];
|
|
if ((def->_is_alpha)&&(def->_opposite != 0)) {
|
|
// This texture represents an alpha-filename. It doesn't get its own <Texture>
|
|
continue;
|
|
}
|
|
|
|
EggTexture tex(shader.get_name(), "");
|
|
tex.set_format(def->_is_alpha ? EggTexture::F_alpha : EggTexture::F_rgb);
|
|
apply_texture_filename(tex, *def);
|
|
if (def->_opposite) {
|
|
apply_texture_alpha_filename(tex, *def);
|
|
}
|
|
apply_texture_uvprops(tex, *def);
|
|
apply_texture_blendtype(tex, *def);
|
|
tex.set_uv_name(def->get_panda_uvset_name());
|
|
|
|
EggTexture *new_tex =
|
|
_textures.create_unique_texture(tex, ~0);
|
|
primitive.add_texture(new_tex);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: MayaToEggConverter::set_shader_legacy
|
|
// Access: Private
|
|
// Description: The legacy implementation of set_shader_attributes.
|
|
// The old behavior of the exporter is just plain weird.
|
|
// It seems to be a result of an inexperienced coder
|
|
// who made some core mistakes, and then patched them
|
|
// up with kludges. It seems to produce plausible
|
|
// results in certain specific cases, but overall, it
|
|
// doesn't make any sense. Unfortunately, this weird
|
|
// behavior cannot be discarded - vast numbers of 3D
|
|
// models have been created that rely on this behavior.
|
|
// The solution is to compartmentalize the weirdness.
|
|
// The legacy codepath, when activated, implements the
|
|
// old weird behavior. A brand-new codepath that
|
|
// shares almost nothing with the legacy codepath
|
|
// implements a much more straightforward behavior.
|
|
////////////////////////////////////////////////////////////////////
|
|
void MayaToEggConverter::
|
|
set_shader_legacy(EggPrimitive &primitive, const MayaShader &shader,
|
|
bool mesh) {
|
|
|
|
// determine if the base texture or any of the top texture need to be rgb only
|
|
MayaShaderColorDef *color_def = NULL;
|
|
bool is_rgb = false;
|
|
bool is_decal = false;
|
|
bool is_interpolate = false;
|
|
int i;
|
|
// last shader is the base so lets skip it
|
|
for (i=0; i<(int)shader._color.size()-1; ++i) {
|
|
color_def = shader.get_color_def(i);
|
|
if (color_def->_has_texture) {
|
|
if ((EggTexture::EnvType)color_def->_interpolate) {
|
|
is_interpolate = true;
|
|
}
|
|
else if ((EggTexture::EnvType)color_def->_blend_type == EggTexture::ET_modulate) {
|
|
// Maya's multiply is slightly different than panda's. Unless, _keep_alpha is set,
|
|
// we are dropping the alpha.
|
|
if (!color_def->_keep_alpha)
|
|
is_rgb = true; // modulate forces the alpha to be ignored
|
|
}
|
|
else if ((EggTexture::EnvType)color_def->_blend_type == EggTexture::ET_decal) {
|
|
is_decal = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// we don't want an extra light stage for interpolate mode, it takes care of automatically
|
|
if (is_interpolate)
|
|
is_decal = false;
|
|
|
|
// new decal mode needs an extra dummy layers of textureStage
|
|
EggTexture *dummy_tex = (EggTexture *)NULL;
|
|
string dummy_uvset_name;
|
|
|
|
// In Maya, a polygon is either textured or colored. The texture,
|
|
// if present, replaces the color. Also now there could be multiple textures
|
|
const MayaShaderColorDef &trans_def = shader._transparency;
|
|
for (i=shader._color.size()-1; i>=0; --i) {
|
|
color_def = shader.get_color_def(i);
|
|
if (mayaegg_cat.is_spam()) {
|
|
mayaegg_cat.spam() << "slot " << i << ":got color_def: " << color_def << endl;
|
|
}
|
|
if (color_def->_has_texture || trans_def._has_texture) {
|
|
EggTexture tex(shader.get_name(), "");
|
|
if (mayaegg_cat.is_debug()) {
|
|
mayaegg_cat.debug() << "got shader name:" << shader.get_name() << endl;
|
|
mayaegg_cat.debug() << "ssa:texture name[" << i << "]: " << color_def->_texture_name << endl;
|
|
}
|
|
|
|
string uvset_name = _shaders.find_uv_link(color_def->_texture_name);
|
|
if (mayaegg_cat.is_debug()) {
|
|
mayaegg_cat.debug() << "ssa:corresponding uvset name is " << uvset_name << endl;
|
|
}
|
|
|
|
if (color_def->_has_texture) {
|
|
// If we have a texture on color, apply it as the filename.
|
|
//if (mayaegg_cat.is_debug()) {
|
|
//mayaegg_cat.debug() << "ssa:got texture name" << color_def->_texture_filename << endl;
|
|
//}
|
|
Filename filename = Filename::from_os_specific(color_def->_texture_filename);
|
|
Filename fullpath, outpath;
|
|
_path_replace->full_convert_path(filename, get_texture_path(), fullpath, outpath);
|
|
tex.set_filename(outpath);
|
|
tex.set_fullpath(fullpath);
|
|
apply_texture_uvprops(tex, *color_def);
|
|
|
|
// If we also have a texture on transparency, apply it as the
|
|
// alpha filename.
|
|
if (trans_def._has_texture) {
|
|
if (color_def->_wrap_u != trans_def._wrap_u ||
|
|
color_def->_wrap_u != trans_def._wrap_u) {
|
|
mayaegg_cat.warning()
|
|
<< "Shader " << shader.get_name()
|
|
<< " has contradictory wrap modes on color and texture.\n";
|
|
}
|
|
|
|
if (!compare_texture_uvprops(tex, trans_def)) {
|
|
// Only report each broken shader once.
|
|
static pset<string> bad_shaders;
|
|
if (bad_shaders.insert(shader.get_name()).second) {
|
|
mayaegg_cat.error()
|
|
<< "Color and transparency texture properties differ on shader "
|
|
<< shader.get_name() << "\n";
|
|
}
|
|
}
|
|
//tex.set_format(EggTexture::F_rgba);
|
|
|
|
// We should try to be smarter about whether the transparency
|
|
// value is connected to the texture's alpha channel or to its
|
|
// grayscale channel. However, I'm not sure how to detect
|
|
// this at the moment; rather than spending days trying to
|
|
// figure out, for now I'll just assume that if the same
|
|
// texture image is used for both color and transparency, then
|
|
// the artist meant to use the alpha channel for transparency.
|
|
if (trans_def._texture_filename == color_def->_texture_filename) {
|
|
// That means that we don't need to do anything special: use
|
|
// all the channels of the texture.
|
|
|
|
} else {
|
|
// Otherwise, pull the alpha channel from the other image
|
|
// file. Ideally, we should figure out which channel from
|
|
// the other image supplies alpha (and specify this via
|
|
// set_alpha_file_channel()), but for now we assume it comes
|
|
// from the grayscale data.
|
|
filename = Filename::from_os_specific(trans_def._texture_filename);
|
|
_path_replace->full_convert_path(filename, get_texture_path(),
|
|
fullpath, outpath);
|
|
tex.set_alpha_filename(outpath);
|
|
tex.set_alpha_fullpath(fullpath);
|
|
}
|
|
} else {
|
|
// If there is no transparency texture specified, we don't
|
|
// have any transparency, so tell the egg format to ignore any
|
|
// alpha channel that might be on the color texture.
|
|
//tex.set_format(EggTexture::F_rgb);
|
|
}
|
|
|
|
if (shader._color.size() > 1) {
|
|
// if multi-textured, first texture in maya is on top, so
|
|
// last shader on the list is the base one, which should always pick up the alpha
|
|
// from the texture file. But the top textures may have to strip the alpha
|
|
if (i!=shader._color.size()-1) {
|
|
if (!i && is_interpolate) {
|
|
// this is the grass path mode where alpha on this texture determines
|
|
// whether to show layer1 or layer2. Since by now other layers are set
|
|
// lets change those to get this effect
|
|
tex.set_combine_mode(EggTexture::CC_rgb, EggTexture::CM_interpolate);
|
|
tex.set_combine_source(EggTexture::CC_rgb, 0, EggTexture::CS_previous);
|
|
tex.set_combine_operand(EggTexture::CC_rgb, 0, EggTexture::CO_src_color);
|
|
tex.set_combine_source(EggTexture::CC_rgb, 1, EggTexture::CS_last_saved_result);
|
|
tex.set_combine_operand(EggTexture::CC_rgb, 1, EggTexture::CO_src_color);
|
|
tex.set_combine_source(EggTexture::CC_rgb, 2, EggTexture::CS_texture);
|
|
tex.set_combine_operand(EggTexture::CC_rgb, 2, EggTexture::CO_src_alpha);
|
|
|
|
tex.set_combine_mode(EggTexture::CC_alpha, EggTexture::CM_interpolate);
|
|
tex.set_combine_source(EggTexture::CC_alpha, 0, EggTexture::CS_previous);
|
|
tex.set_combine_operand(EggTexture::CC_alpha, 0, EggTexture::CO_src_alpha);
|
|
tex.set_combine_source(EggTexture::CC_alpha, 1, EggTexture::CS_last_saved_result);
|
|
tex.set_combine_operand(EggTexture::CC_alpha, 1, EggTexture::CO_src_alpha);
|
|
tex.set_combine_source(EggTexture::CC_alpha, 2, EggTexture::CS_texture);
|
|
tex.set_combine_operand(EggTexture::CC_alpha, 2, EggTexture::CO_src_alpha);
|
|
}
|
|
else {
|
|
if (is_interpolate) {
|
|
tex.set_combine_mode(EggTexture::CC_rgb, EggTexture::CM_modulate);
|
|
tex.set_combine_source(EggTexture::CC_rgb, 0, EggTexture::CS_primary_color);
|
|
tex.set_combine_operand(EggTexture::CC_rgb, 0, EggTexture::CO_src_color);
|
|
tex.set_combine_source(EggTexture::CC_rgb, 1, EggTexture::CS_texture);
|
|
tex.set_combine_operand(EggTexture::CC_rgb, 1, EggTexture::CO_src_color);
|
|
}
|
|
else {
|
|
tex.set_env_type((EggTexture::EnvType)color_def->_blend_type);
|
|
if (tex.get_env_type() == EggTexture::ET_modulate) {
|
|
if (color_def->_has_alpha_channel) {
|
|
// lets caution the artist that they should not be using a alpha channel on
|
|
// this texture.
|
|
if (mayaegg_cat.is_spam()) {
|
|
maya_cat.spam()
|
|
<< color_def->_texture_name
|
|
<< " should not have alpha channel in multiply mode: ignoring\n";
|
|
}
|
|
}
|
|
if (is_rgb) {
|
|
//tex.set_alpha_mode(EggRenderMode::AM_off); // force alpha off
|
|
tex.set_format(EggTexture::F_rgb); // Change the format to be rgb only
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (is_interpolate) {
|
|
// base shader need to save result
|
|
tex.set_saved_result(true);
|
|
}
|
|
else if (is_decal) {
|
|
// decal in classic time, always overwrote the base color. That causes problem
|
|
// when the polygon wants to be lit or wants to retain vertex/polygon color
|
|
// In the new decal mode, we achieve this with a third dummy layer
|
|
// copy this layer to a new dummy layer
|
|
EggTexture texDummy(shader.get_name()+".dummy", "");
|
|
if (mayaegg_cat.is_debug()) {
|
|
mayaegg_cat.debug() << "creating dummy shader: " << texDummy.get_name() << endl;
|
|
}
|
|
texDummy.set_filename(outpath);
|
|
texDummy.set_fullpath(fullpath);
|
|
apply_texture_uvprops(texDummy, *color_def);
|
|
texDummy.set_combine_mode(EggTexture::CC_rgb, EggTexture::CM_modulate);
|
|
texDummy.set_combine_source(EggTexture::CC_rgb, 0, EggTexture::CS_primary_color);
|
|
texDummy.set_combine_operand(EggTexture::CC_rgb, 0, EggTexture::CO_src_color);
|
|
texDummy.set_combine_source(EggTexture::CC_rgb, 1, EggTexture::CS_previous);
|
|
texDummy.set_combine_operand(EggTexture::CC_rgb, 1, EggTexture::CO_src_color);
|
|
dummy_tex = _textures.create_unique_texture(texDummy, ~0);
|
|
|
|
// make this layer ET_replace
|
|
tex.set_env_type(EggTexture::ET_replace);
|
|
}
|
|
}
|
|
}
|
|
} else { // trans_def._has_texture
|
|
// We have a texture on transparency only. Apply it as the
|
|
// primary filename, and set the format accordingly.
|
|
Filename filename = Filename::from_os_specific(trans_def._texture_filename);
|
|
Filename fullpath,outpath;
|
|
_path_replace->full_convert_path(filename, get_texture_path(),
|
|
fullpath, outpath);
|
|
tex.set_filename(outpath);
|
|
tex.set_fullpath(fullpath);
|
|
tex.set_format(EggTexture::F_alpha);
|
|
apply_texture_uvprops(tex, trans_def);
|
|
}
|
|
|
|
if (mayaegg_cat.is_debug()) {
|
|
mayaegg_cat.debug() << "ssa:tref_name:" << tex.get_name() << endl;
|
|
}
|
|
if (is_rgb && i == (int)shader._color.size()-1) {
|
|
// make base layer rgb only
|
|
tex.set_format(EggTexture::F_rgb); // Change the format to be rgb only
|
|
}
|
|
EggTexture *new_tex =
|
|
_textures.create_unique_texture(tex, ~0);
|
|
|
|
if (mesh) {
|
|
if (uvset_name.find("not found") == -1) {
|
|
primitive.add_texture(new_tex);
|
|
color_def->_uvset_name.assign(uvset_name.c_str());
|
|
if (uvset_name != "map1") {
|
|
new_tex->set_uv_name(uvset_name);
|
|
}
|
|
if (i == (int)shader._color.size()-1 && is_decal) {
|
|
dummy_uvset_name.assign(color_def->_uvset_name);
|
|
}
|
|
}
|
|
} else {
|
|
primitive.add_texture(new_tex);
|
|
new_tex->set_uv_name(color_def->_uvset_name);
|
|
}
|
|
}
|
|
}
|
|
if (dummy_tex != (EggTexture *)NULL) {
|
|
primitive.add_texture(dummy_tex);
|
|
dummy_tex->set_uv_name(dummy_uvset_name);
|
|
}
|
|
// Also apply an overall color to the primitive.
|
|
Colorf rgba = shader.get_rgba();
|
|
if (mayaegg_cat.is_spam()) {
|
|
mayaegg_cat.spam() << "ssa:rgba = " << rgba << endl;
|
|
}
|
|
|
|
// The existence of a texture on either color channel completely
|
|
// replaces the corresponding flat color.
|
|
if (color_def && color_def->_has_texture) {
|
|
rgba[0] = 1.0f;
|
|
rgba[1] = 1.0f;
|
|
rgba[2] = 1.0f;
|
|
}
|
|
if (trans_def._has_texture) {
|
|
rgba[3] = 1.0f;
|
|
}
|
|
|
|
// But the color gain always gets applied.
|
|
if (color_def) {
|
|
rgba[0] *= color_def->_color_gain[0];
|
|
rgba[1] *= color_def->_color_gain[1];
|
|
rgba[2] *= color_def->_color_gain[2];
|
|
rgba[3] *= color_def->_color_gain[3];
|
|
if (mayaegg_cat.is_spam()) {
|
|
mayaegg_cat.spam() << "ssa:rgba = " << rgba << endl;
|
|
}
|
|
}
|
|
|
|
primitive.set_color(rgba);
|
|
|
|
if (mayaegg_cat.is_spam()) {
|
|
mayaegg_cat.spam() << " set_shader_attributes : end\n";
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: MayaShader::apply_texture_uvprops
|
|
// Access: Private
|
|
// Description: Applies all the appropriate texture properties to the
|
|
// EggTexture object, including wrap modes and texture
|
|
// matrix.
|
|
////////////////////////////////////////////////////////////////////
|
|
void MayaToEggConverter::
|
|
apply_texture_uvprops(EggTexture &tex, const MayaShaderColorDef &color_def) {
|
|
// Let's mipmap all textures by default.
|
|
tex.set_minfilter(EggTexture::FT_linear_mipmap_linear);
|
|
tex.set_magfilter(EggTexture::FT_linear);
|
|
|
|
EggTexture::WrapMode wrap_u = color_def._wrap_u ? EggTexture::WM_repeat : EggTexture::WM_clamp;
|
|
EggTexture::WrapMode wrap_v = color_def._wrap_v ? EggTexture::WM_repeat : EggTexture::WM_clamp;
|
|
|
|
tex.set_wrap_u(wrap_u);
|
|
tex.set_wrap_v(wrap_v);
|
|
|
|
LMatrix3d mat = color_def.compute_texture_matrix();
|
|
if (!mat.almost_equal(LMatrix3d::ident_mat())) {
|
|
tex.set_transform2d(mat);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: MayaShader::apply_texture_blendtype
|
|
// Access: Private
|
|
// Description: Applies the blendtype to the EggTexture.
|
|
////////////////////////////////////////////////////////////////////
|
|
void MayaToEggConverter::
|
|
apply_texture_blendtype(EggTexture &tex, const MayaShaderColorDef &color_def) {
|
|
switch (color_def._blend_type) {
|
|
case MayaShaderColorDef::BT_unspecified:
|
|
tex.set_env_type(EggTexture::ET_unspecified);
|
|
return;
|
|
case MayaShaderColorDef::BT_modulate:
|
|
tex.set_env_type(EggTexture::ET_modulate);
|
|
return;
|
|
case MayaShaderColorDef::BT_decal:
|
|
tex.set_env_type(EggTexture::ET_decal);
|
|
return;
|
|
case MayaShaderColorDef::BT_blend:
|
|
tex.set_env_type(EggTexture::ET_blend);
|
|
return;
|
|
case MayaShaderColorDef::BT_replace:
|
|
tex.set_env_type(EggTexture::ET_replace);
|
|
return;
|
|
case MayaShaderColorDef::BT_add:
|
|
tex.set_env_type(EggTexture::ET_add);
|
|
return;
|
|
case MayaShaderColorDef::BT_blend_color_scale:
|
|
tex.set_env_type(EggTexture::ET_blend_color_scale);
|
|
return;
|
|
case MayaShaderColorDef::BT_modulate_glow_map:
|
|
tex.set_env_type(EggTexture::ET_modulate_glow_map);
|
|
return;
|
|
case MayaShaderColorDef::BT_normal_map:
|
|
tex.set_env_type(EggTexture::ET_normal_map);
|
|
return;
|
|
case MayaShaderColorDef::BT_gloss_map:
|
|
tex.set_env_type(EggTexture::ET_gloss_map);
|
|
return;
|
|
case MayaShaderColorDef::BT_glow_map:
|
|
tex.set_env_type(EggTexture::ET_glow_map);
|
|
return;
|
|
case MayaShaderColorDef::BT_normal_gloss_map:
|
|
tex.set_env_type(EggTexture::ET_normal_gloss_map);
|
|
return;
|
|
case MayaShaderColorDef::BT_selector_map:
|
|
tex.set_env_type(EggTexture::ET_selector_map);
|
|
return;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: MayaShader::apply_texture_filename
|
|
// Access: Private
|
|
// Description: Applies the filename to the EggTexture.
|
|
////////////////////////////////////////////////////////////////////
|
|
void MayaToEggConverter::
|
|
apply_texture_filename(EggTexture &tex, const MayaShaderColorDef &def) {
|
|
Filename filename = Filename::from_os_specific(def._texture_filename);
|
|
Filename fullpath, outpath;
|
|
_path_replace->full_convert_path(filename, get_texture_path(), fullpath, outpath);
|
|
tex.set_filename(outpath);
|
|
tex.set_fullpath(fullpath);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: MayaShader::apply_texture_alpha_filename
|
|
// Access: Private
|
|
// Description: Applies the alpha filename to the EggTexture.
|
|
////////////////////////////////////////////////////////////////////
|
|
void MayaToEggConverter::
|
|
apply_texture_alpha_filename(EggTexture &tex, const MayaShaderColorDef &def) {
|
|
if (def._opposite) {
|
|
tex.set_format(EggTexture::F_rgba);
|
|
if (def._opposite->_texture_filename != def._texture_filename) {
|
|
Filename filename = Filename::from_os_specific(def._opposite->_texture_filename);
|
|
Filename fullpath, outpath;
|
|
_path_replace->full_convert_path(filename, get_texture_path(), fullpath, outpath);
|
|
tex.set_alpha_filename(outpath);
|
|
tex.set_alpha_fullpath(fullpath);
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: MayaShader::compare_texture_uvprops
|
|
// Access: Private
|
|
// Description: Compares the texture properties already on the
|
|
// texture (presumably set by a previous call to
|
|
// apply_texture_uvprops()) and returns false if they
|
|
// differ from that specified by the indicated color_def
|
|
// object, or true if they match.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool MayaToEggConverter::
|
|
compare_texture_uvprops(EggTexture &tex,
|
|
const MayaShaderColorDef &color_def) {
|
|
bool okflag = true;
|
|
|
|
EggTexture::WrapMode wrap_u = color_def._wrap_u ? EggTexture::WM_repeat : EggTexture::WM_clamp;
|
|
EggTexture::WrapMode wrap_v = color_def._wrap_v ? EggTexture::WM_repeat : EggTexture::WM_clamp;
|
|
|
|
if (wrap_u != tex.determine_wrap_u()) {
|
|
// Choose the more general of the two.
|
|
if (wrap_u == EggTexture::WM_repeat) {
|
|
tex.set_wrap_u(wrap_u);
|
|
}
|
|
okflag = false;
|
|
}
|
|
if (wrap_v != tex.determine_wrap_v()) {
|
|
if (wrap_v == EggTexture::WM_repeat) {
|
|
tex.set_wrap_v(wrap_v);
|
|
}
|
|
okflag = false;
|
|
}
|
|
|
|
LMatrix3d m = color_def.compute_texture_matrix();
|
|
LMatrix4d mat4(m(0, 0), m(0, 1), 0.0, m(0, 2),
|
|
m(1, 0), m(1, 1), 0.0, m(1, 2),
|
|
0.0, 0.0, 1.0, 0.0,
|
|
m(2, 0), m(2, 1), 0.0, m(2, 2));
|
|
if (!mat4.almost_equal(tex.get_transform3d())) {
|
|
okflag = false;
|
|
}
|
|
|
|
return okflag;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: MayaShader::reparent_decals
|
|
// Access: Private
|
|
// Description: Recursively walks the egg hierarchy, reparenting
|
|
// "decal" type nodes below their corresponding
|
|
// "decalbase" type nodes, and setting the flags.
|
|
//
|
|
// Returns true on success, false if some nodes were
|
|
// incorrect.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool MayaToEggConverter::
|
|
reparent_decals(EggGroupNode *egg_parent) {
|
|
bool okflag = true;
|
|
|
|
// First, walk through all children of this node, looking for the
|
|
// one decal base, if any.
|
|
EggGroup *decal_base = (EggGroup *)NULL;
|
|
pvector<EggGroup *> decal_children;
|
|
|
|
EggGroupNode::iterator ci;
|
|
for (ci = egg_parent->begin(); ci != egg_parent->end(); ++ci) {
|
|
EggNode *child = (*ci);
|
|
if (child->is_of_type(EggGroup::get_class_type())) {
|
|
EggGroup *child_group = DCAST(EggGroup, child);
|
|
if (child_group->has_object_type("decalbase")) {
|
|
if (decal_base != (EggNode *)NULL) {
|
|
mayaegg_cat.error()
|
|
<< "Two children of " << egg_parent->get_name()
|
|
<< " both have decalbase set: " << decal_base->get_name()
|
|
<< " and " << child_group->get_name() << "\n";
|
|
okflag = false;
|
|
}
|
|
child_group->remove_object_type("decalbase");
|
|
decal_base = child_group;
|
|
|
|
} else if (child_group->has_object_type("decal")) {
|
|
child_group->remove_object_type("decal");
|
|
decal_children.push_back(child_group);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (decal_base == (EggGroup *)NULL) {
|
|
if (!decal_children.empty()) {
|
|
mayaegg_cat.warning()
|
|
<< decal_children.front()->get_name()
|
|
<< " has decal, but no sibling node has decalbase.\n";
|
|
}
|
|
|
|
} else {
|
|
if (decal_children.empty()) {
|
|
mayaegg_cat.warning()
|
|
<< decal_base->get_name()
|
|
<< " has decalbase, but no sibling nodes have decal.\n";
|
|
|
|
} else {
|
|
// All the decal children get moved to be a child of decal base.
|
|
// This usually will not affect the vertex positions, but it
|
|
// could if the decal base has a transform and the decal child
|
|
// is an instance node. So don't do that.
|
|
pvector<EggGroup *>::iterator di;
|
|
for (di = decal_children.begin(); di != decal_children.end(); ++di) {
|
|
EggGroup *child_group = (*di);
|
|
decal_base->add_child(child_group);
|
|
}
|
|
|
|
// Also set the decal state on the base.
|
|
decal_base->set_decal_flag(true);
|
|
}
|
|
}
|
|
|
|
// Now recurse on each of the child nodes.
|
|
for (ci = egg_parent->begin(); ci != egg_parent->end(); ++ci) {
|
|
EggNode *child = (*ci);
|
|
if (child->is_of_type(EggGroupNode::get_class_type())) {
|
|
EggGroupNode *child_group = DCAST(EggGroupNode, child);
|
|
if (!reparent_decals(child_group)) {
|
|
okflag = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return okflag;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: MayaShader::string_transform_type
|
|
// Access: Public, Static
|
|
// Description: Returns the TransformType value corresponding to the
|
|
// indicated string, or TT_invalid.
|
|
////////////////////////////////////////////////////////////////////
|
|
MayaToEggConverter::TransformType MayaToEggConverter::
|
|
string_transform_type(const string &arg) {
|
|
if (cmp_nocase(arg, "all") == 0) {
|
|
return TT_all;
|
|
} else if (cmp_nocase(arg, "model") == 0) {
|
|
return TT_model;
|
|
} else if (cmp_nocase(arg, "dcs") == 0) {
|
|
return TT_dcs;
|
|
} else if (cmp_nocase(arg, "none") == 0) {
|
|
return TT_none;
|
|
} else {
|
|
return TT_invalid;
|
|
}
|
|
}
|