21 #include "pandatoolbase.h" 22 #include "notifyCategoryProxy.h" 25 #include "eggVertexPool.h" 26 #include "eggVertex.h" 27 #include "eggPolygon.h" 28 #include "eggPrimitive.h" 29 #include "eggGroupNode.h" 30 #include "eggPolysetMaker.h" 44 #include "maxEggLoader.h" 50 NotifyCategoryDeclNoExport(maxloader);
51 NotifyCategoryDef(maxloader,
"");
56 bool ConvertEggData(
EggData *data,
bool merge,
bool model,
bool anim);
57 bool ConvertEggFile(
const char *name,
bool merge,
bool model,
bool anim);
67 typedef phash_map<EggVertexPool *, MaxEggMesh *> MeshTable;
69 typedef phash_map<EggGroup *, MaxEggJoint *> JointTable;
71 typedef phash_map<string, MaxEggTex *> TexTable;
75 JointTable _joint_tab;
82 return Point3(vec[0], vec[1], vec[2]);
102 if (_tex_tab.count(fn))
105 BitmapTex *bmt = NewDefaultBitmapTex();
112 StdMat *mat = NewDefaultStdMat();
113 mat->SetSubTexmap(ID_DI, bmt);
114 mat->SetTexmapAmt(ID_DI, 1.0, 0);
115 mat->EnableMap(ID_DI, TRUE);
116 mat->SetActiveTexmap(bmt);
117 GetCOREInterface()->ActivateTexture(bmt, mat);
121 res->_id = _next_tex ++;
143 SimpleObject2 *_bone;
147 vector <MaxEggJoint *> _children;
153 void ChooseEndPos(
double thickness);
154 void CreateMaxBone(
void);
159 xv = _trans.get_row3(0);
160 yv = _trans.get_row3(1);
161 zv = _trans.get_row3(2);
171 if (joint==0)
return 0;
172 return _joint_tab[joint];
181 result->_trans = t * parent->_trans;
187 result->_thickness = 0.0;
188 result->_inskin =
false;
191 result->_egg_joint = joint;
192 result->_parent = parent;
193 if (parent) parent->_children.push_back(result);
194 _joint_tab[joint] = result;
200 if (dir.
length() < 0.001)
return 0;
202 double firstbest = -1000;
205 double secondbest = 0;
206 for (
int i=0; i<_children.size(); i++) {
208 LVector3d tryfwd = child->GetPos() - GetPos();
209 if ((child->GetPos() != firstpos) && (tryfwd.
length() > 0.001)) {
212 double quality = trydir.dot(dir);
213 if (quality > firstbest) {
214 secondbest = firstbest;
216 firstpos = child->GetPos();
218 }
else if (quality > secondbest) {
219 secondbest = quality;
223 if (firstbest > secondbest + 0.1)
228 void MaxEggJoint::ChooseEndPos(
double thickness)
233 parentpos = _parent->GetPos();
234 parentendpos = _parent->_endpos;
237 if (fwd.
length() < 0.001) {
238 fwd = parentendpos - parentpos;
243 _endpos = fwd * thickness * 0.8 + GetPos();
244 _thickness = thickness * 0.8;
246 _endpos = child->GetPos();
247 _thickness = (_endpos - GetPos()).length();
248 if (_thickness > thickness) _thickness = thickness;
253 if (altaxis.
length() < 0.001) altaxis = orient.cross(
LVector3d(0,0,1));
254 _perp = altaxis.cross(orient);
258 void MaxEggJoint::CreateMaxBone(
void)
261 GetRotation(rxv, ryv, rzv);
262 Point3 xv(MakeMaxPoint(rxv));
263 Point3 yv(MakeMaxPoint(ryv));
264 Point3 zv(MakeMaxPoint(rzv));
265 Point3 pos(MakeMaxPoint(GetPos()));
266 Point3 endpos(MakeMaxPoint(_endpos));
267 Point3 tzv(MakeMaxPoint(_perp));
269 Point3 fwd = endpos - pos;
270 double len = fwd.Length();
271 Point3 txv = fwd * ((PN_stdfloat)(1.0/len));
272 Point3 tyv = tzv ^ txv;
273 Point3 row1 = Point3(txv % xv, txv % yv, txv % zv);
274 Point3 row2 = Point3(tyv % xv, tyv % yv, tyv % zv);
275 Point3 row3 = Point3(tzv % xv, tzv % yv, tzv % zv);
276 Matrix3 oomat(row1,row2,row3,Point3(0,0,0));
278 _bone = (SimpleObject2*)CreateInstance(GEOMOBJECT_CLASS_ID, BONE_OBJ_CLASSID);
279 _node = GetCOREInterface()->CreateObjectNode(_bone);
280 _node->SetNodeTM(0, Matrix3(xv, yv, zv, pos));
281 IParamBlock2 *blk = _bone->pblock2;
282 for (
int i=0; i<blk->NumParams(); i++) {
283 TSTR n = blk->GetLocalName(i);
284 if (_tcscmp(n, _T(
"Length")) == 0) blk->SetValue(i, 0, (PN_stdfloat) len);
285 else if (_tcscmp(n, _T(
"Width")) == 0) blk->SetValue(i, 0, (PN_stdfloat) _thickness);
286 else if (_tcscmp(n, _T(
"Height")) == 0) blk->SetValue(i, 0, (PN_stdfloat) _thickness);
288 Point3 boneColor = GetUIColor(COLOR_BONES);
289 _node->SetWireColor(RGB(
int(boneColor.x*255.0f),
int(boneColor.y*255.0f),
int(boneColor.z*255.0f) ));
290 _node->SetBoneNodeOnOff(TRUE, 0);
291 _node->SetRenderable(FALSE);
296 mbstowcs(wname, _egg_joint->get_name().c_str(), 1023);
297 _node->SetName(wname);
299 _node->SetName((
char*) _egg_joint->get_name().c_str());
302 _node->SetObjOffsetRot(ooquat);
305 _parent->_node->AttachChild(_node, 1);
315 typedef pair<double, EggGroup *> MaxEggWeight;
321 vector<MaxEggWeight> _weights;
334 if (n < 0)
return true;
335 if (n > 0)
return false;
337 if (n < 0)
return true;
338 if (n > 0)
return false;
339 n = k1._weights.size() - k2._weights.size();
340 if (n < 0)
return true;
341 if (n > 0)
return false;
342 for (
int i=0; i<k1._weights.size(); i++) {
343 double d = k1._weights[i].first - k2._weights[i].first;
344 if (d < 0)
return true;
345 if (d > 0)
return false;
346 EggGroup *g1 = k1._weights[i].second;
347 EggGroup *g2 = k2._weights[i].second;
348 if (g1 < g2)
return true;
349 if (g1 > g2)
return false;
355 typedef phash_set<MaxEggVertex, MEV_Compare> VertTable;
356 typedef phash_map<LTexCoordd, int> TVertTable;
357 typedef phash_map<LColor, int> CVertTable;
367 IDerivedObject *_dobj;
370 ISkinImportData *_iskin_import;
377 TVertTable _tvert_tab;
378 CVertTable _cvert_tab;
382 int GetCVert(
const LColor &col);
383 int AddFace(
int v0,
int v1,
int v2,
int tv0,
int tv1,
int tv2,
int cv0,
int cv1,
int cv2,
int tex);
387 #define CTRLJOINT_DEFORM ((EggGroup*)((char*)(-1))) 393 vtx._normal = vert->get_normal();
396 EggVertex::GroupRef::const_iterator gri;
400 vtx._weights.push_back(MaxEggWeight(membership, egg_joint));
402 if (vtx._weights.size()==0) {
404 vtx._weights.push_back(MaxEggWeight(1.0, context));
407 VertTable::const_iterator vti = _vert_tab.find(vtx);
408 if (vti != _vert_tab.end())
411 if (_vert_count == _mesh->numVerts) {
412 int nsize = _vert_count*2 + 100;
413 _mesh->setNumVerts(nsize, _vert_count?TRUE:FALSE);
415 vtx._index = _vert_count++;
416 _vert_tab.insert(vtx);
417 _mesh->setVert(vtx._index, MakeMaxPoint(vtx._pos));
421 int MaxEggMesh::GetTVert(
const LTexCoordd &uv)
423 if (_tvert_tab.count(uv))
424 return _tvert_tab[uv];
425 if (_tvert_count == _mesh->numTVerts) {
426 int nsize = _tvert_count*2 + 100;
427 _mesh->setNumTVerts(nsize, _tvert_count?TRUE:FALSE);
429 int idx = _tvert_count++;
430 _mesh->setTVert(idx, uv.get_x(), uv.get_y(), 0.0);
431 _tvert_tab[uv] = idx;
435 int MaxEggMesh::GetCVert(
const LColor &col)
437 if (_cvert_tab.count(col))
438 return _cvert_tab[col];
439 if (_cvert_count == _mesh->numCVerts) {
440 int nsize = _cvert_count*2 + 100;
441 _mesh->setNumVertCol(nsize, _cvert_count?TRUE:FALSE);
443 int idx = _cvert_count++;
444 _mesh->vertCol[idx] = Point3(col.get_x(), col.get_y(), col.get_z());
445 _cvert_tab[col] = idx;
453 string name = pool->get_name();
454 int nsize = name.size();
455 if ((nsize > 6) && (name.rfind(
".verts")==(nsize-6)))
456 name.resize(nsize-6);
458 result->_name = name;
459 result->_obj = CreateNewTriObject();
460 result->_mesh = &result->_obj->GetMesh();
461 result->_mesh->setMapSupport(0, TRUE);
462 result->_node = GetCOREInterface()->CreateObjectNode(result->_obj);
464 result->_skin_mod = 0;
466 result->_iskin_import = 0;
467 result->_vert_count = 0;
468 result->_tvert_count = 0;
469 result->_cvert_count = 0;
470 result->_face_count = 0;
474 mbstowcs(wname, name.c_str(), 1023);
475 result->_node->SetName(wname);
477 result->_node->SetName((
char*) name.c_str());
479 _mesh_tab[pool] = result;
484 int MaxEggMesh::AddFace(
int v0,
int v1,
int v2,
int tv0,
int tv1,
int tv2,
int cv0,
int cv1,
int cv2,
int tex)
487 if (_face_count == _mesh->numFaces) {
488 int nsize = _face_count*2 + 100;
489 BOOL keep = _mesh->numFaces ? TRUE : FALSE;
490 _mesh->setNumFaces(nsize, keep);
491 _mesh->setNumTVFaces(nsize, keep, _face_count);
492 _mesh->setNumVCFaces(nsize, keep, _face_count);
494 int idx = _face_count++;
495 _mesh->faces[idx].setVerts(v0,v1,v2);
496 _mesh->faces[idx].smGroup = 1;
497 _mesh->faces[idx].flags = EDGE_ALL | HAS_TVERTS;
498 _mesh->faces[idx].setMatID(tex);
499 _mesh->tvFace[idx].setTVerts(tv0,tv1,tv2);
500 _mesh->vcFace[idx].setTVerts(cv0,cv1,cv2);
504 EggGroup *MaxEggMesh::GetControlJoint(
void)
507 VertTable::const_iterator vert = _vert_tab.begin();
508 if (vert == _vert_tab.end())
return 0;
509 switch (vert->_weights.size()) {
511 for (++vert; vert != _vert_tab.end(); ++vert)
512 if (vert->_weights.size() != 0)
513 return CTRLJOINT_DEFORM;
516 result = vert->_weights[0].second;
517 for (++vert; vert != _vert_tab.end(); ++vert)
518 if ((vert->_weights.size() != 1) || (vert->_weights[0].second != result))
519 return CTRLJOINT_DEFORM;
522 return CTRLJOINT_DEFORM;
526 void MaxEggLoader::CreateSkinModifier(
MaxEggMesh *M)
528 vector <MaxEggJoint *> joints;
530 M->_dobj = CreateDerivedObject(M->_obj);
531 M->_node->SetObjectRef(M->_dobj);
532 M->_skin_mod = (Modifier*)CreateInstance(OSM_CLASS_ID, SKIN_CLASSID);
533 M->_iskin = (ISkin*)M->_skin_mod->GetInterface(I_SKIN);
534 M->_iskin_import = (ISkinImportData*)M->_skin_mod->GetInterface(I_SKINIMPORTDATA);
535 M->_dobj->SetAFlag(A_LOCK_TARGET);
536 M->_dobj->AddModifier(M->_skin_mod);
537 M->_dobj->ClearAFlag(A_LOCK_TARGET);
538 GetCOREInterface()->ForceCompleteRedraw();
540 VertTable::const_iterator vert;
541 for (vert=M->_vert_tab.begin(); vert != M->_vert_tab.end(); ++vert) {
542 for (
int i=0; i<vert->_weights.size(); i++) {
543 double strength = vert->_weights[i].first;
544 MaxEggJoint *joint = FindJoint(vert->_weights[i].second);
545 if (!joint->_inskin) {
546 joint->_inskin =
true;
547 joints.push_back(joint);
551 for (
int i=0; i<joints.size(); i++) {
552 BOOL last = (i == (joints.size()-1)) ? TRUE : FALSE;
553 M->_iskin_import->AddBoneEx(joints[i]->_node, last);
554 joints[i]->_inskin =
false;
557 GetCOREInterface()->SetCommandPanelTaskMode(TASK_MODE_MODIFY);
558 GetCOREInterface()->SelectNode(M->_node);
559 GetCOREInterface()->ForceCompleteRedraw();
561 for (vert=M->_vert_tab.begin(); vert != M->_vert_tab.end(); ++vert) {
562 Tab<INode*> maxJoints;
563 Tab<PN_stdfloat> maxWeights;
564 maxJoints.ZeroCount();
565 maxWeights.ZeroCount();
566 for (
int i=0; i<vert->_weights.size(); i++) {
567 PN_stdfloat strength = (PN_stdfloat)(vert->_weights[i].first);
568 MaxEggJoint *joint = FindJoint(vert->_weights[i].second);
569 maxWeights.Append(1,&strength);
570 maxJoints.Append(1,&(joint->_node));
572 M->_iskin_import->AddWeights(M->_node, vert->_index, maxJoints, maxWeights);
587 vector<int> vertIndices;
588 vector<int> tvertIndices;
589 vector<int> cvertIndices;
591 if (node->
is_of_type(EggPolygon::get_class_type())) {
605 EggPolygon::const_iterator ci;
608 tvertIndices.clear();
609 cvertIndices.clear();
610 for (ci = poly->begin(); ci != poly->end(); ++ci) {
614 vertIndices.push_back(mesh->GetVert(vtx, context));
615 tvertIndices.push_back(mesh->GetTVert(uv * uvtrans));
616 cvertIndices.push_back(mesh->GetCVert(vtx->
get_color()));
618 for (
int i=1; i<vertIndices.size()-1; i++)
619 mesh->AddFace(vertIndices[0], vertIndices[i], vertIndices[i+1],
620 tvertIndices[0], tvertIndices[i], tvertIndices[i+1],
621 cvertIndices[0], cvertIndices[i], cvertIndices[i+1],
623 }
else if (node->
is_of_type(EggGroupNode::get_class_type())) {
625 if (node->
is_of_type(EggGroup::get_class_type())) {
628 MakeJoint(group, context);
632 EggGroupNode::const_iterator ci;
633 for (ci = group->begin(); ci != group->end(); ++ci) {
634 TraverseEggNode(*ci, context);
639 bool MaxEggLoader::ConvertEggData(
EggData *data,
bool merge,
bool model,
bool anim)
642 maxloader_cat.error() <<
"Currently, only 'merge' mode is implemented.\n";
646 if ((anim) || (!model)) {
647 maxloader_cat.error() <<
"Currently, only model-loading is implemented.\n";
662 TraverseEggNode(data, NULL);
664 for (ci = _mesh_tab.begin(); ci != _mesh_tab.end(); ++ci) {
666 mesh->_mesh->setNumVerts(mesh->_vert_count, TRUE);
667 mesh->_mesh->setNumTVerts(mesh->_tvert_count, TRUE);
668 mesh->_mesh->setNumVertCol(mesh->_cvert_count, TRUE);
669 mesh->_mesh->setNumFaces(mesh->_face_count, TRUE);
670 mesh->_mesh->setNumTVFaces(mesh->_face_count, TRUE, mesh->_face_count);
671 mesh->_mesh->setNumVCFaces(mesh->_face_count, TRUE, mesh->_face_count);
672 mesh->_mesh->InvalidateTopologyCache();
673 mesh->_mesh->InvalidateGeomCache();
674 mesh->_mesh->buildNormals();
677 double thickness = 0.0;
678 for (ji = _joint_tab.begin(); ji != _joint_tab.end(); ++ji) {
679 double dfo = ((*ji)->GetPos()).length();
680 if (dfo > thickness) thickness = dfo;
682 thickness = thickness * 0.025;
683 for (ji = _joint_tab.begin(); ji != _joint_tab.end(); ++ji) {
685 joint->ChooseEndPos(thickness);
686 joint->CreateMaxBone();
689 for (ci = _mesh_tab.begin(); ci != _mesh_tab.end(); ++ci) {
691 EggGroup *joint = mesh->GetControlJoint();
692 if (joint) CreateSkinModifier(mesh);
697 MultiMtl *mtl = NewDefaultMultiMtl();
698 mtl->SetNumSubMtls(_next_tex);
699 for (ti = _tex_tab.begin(); ti != _tex_tab.end(); ++ti) {
701 mtl->SetSubMtlAndName(tex->_id, tex->_mat, name);
703 for (ci = _mesh_tab.begin(); ci != _mesh_tab.end(); ++ci) {
705 mesh->_node->SetMtl(mtl);
709 for (ci = _mesh_tab.begin(); ci != _mesh_tab.end(); ++ci)
delete *ci;
710 for (ji = _joint_tab.begin(); ji != _joint_tab.end(); ++ji)
delete *ji;
711 for (ti = _tex_tab.begin(); ti != _tex_tab.end(); ++ti)
delete *ti;
716 maxloader_cat.info() <<
"Egg import successful\n";
720 bool MaxEggLoader::ConvertEggFile(
const char *name,
bool merge,
bool model,
bool anim)
724 if (!data.
read(datafn)) {
725 maxloader_cat.error() <<
"Cannot read Egg file for import\n";
728 return ConvertEggData(&data, merge, model, anim);
737 bool MaxLoadEggData(
EggData *data,
bool merge,
bool model,
bool anim)
740 return loader.ConvertEggData(data, merge, model, anim);
743 bool MaxLoadEggFile(
const char *name,
bool merge,
bool model,
bool anim)
746 return loader.ConvertEggFile(name, merge, model, anim);
GroupRef::const_iterator gref_end() const
Returns an iterator that can, in conjunction with gref_begin(), be used to traverse the entire set of...
This is an iterator adaptor that converts any iterator that returns a pair (e.g.
virtual bool is_joint() const
Returns true if this particular node represents a <Joint> entry or not.
This is a 4-by-4 transform matrix.
A base class for nodes in the hierarchy that are not leaf nodes.
LTexCoordd get_uv() const
Returns the unnamed UV coordinate pair on the vertex.
Defines a texture map that may be applied to geometry.
EggTexture * get_texture() const
Returns the first texture on the primitive, if any, or NULL if there are no textures on the primitive...
This is a two-component point in space.
GroupRef::const_iterator gref_begin() const
Returns an iterator that can, in conjunction with gref_end(), be used to traverse the entire set of g...
LVertexd get_pos3() const
Valid if get_num_dimensions() returns 3 or 4.
This is the primary interface into all the egg data, and the root of the egg file structure...
LColor get_color() const
Returns the color set on this particular attribute.
wstring to_os_specific_w() const
The wide-string variant on to_os_specific().
size_t get_hash() const
Returns a suitable hash for phash_map.
void set_coordinate_system(CoordinateSystem coordsys)
Changes the coordinate system of the EggData.
double length() const
Returns the length of the vector, by the Pythagorean theorem.
bool normalize()
Normalizes the vector in place.
The main glue of the egg hierarchy, this corresponds to the <Group>, <Instance>, and <Joint> type nod...
This is a 3-by-3 transform matrix.
The name of a file, such as a texture file or an Egg file.
Any one-, two-, three-, or four-component vertex, possibly with attributes such as a normal...
bool read(Filename filename, string display_name=string())
Opens the indicated filename and reads the egg data contents from it.
LVecBase3d get_row3(int row) const
Retrieves the row column of the matrix as a 3-component vector, ignoring the last column...
double get_vertex_membership(const EggVertex *vert) const
Returns the amount of membership of the indicated vertex in this group.
bool has_texture() const
Returns true if the primitive has any textures specified, false otherwise.
const Filename & get_fullpath() const
Returns the full pathname to the file, if it is known; otherwise, returns the same thing as get_filen...
This is the base class for all three-component vectors and points.
This is a three-component vector distance (as opposed to a three-component point, which represents a ...
This is a three-component point in space (as opposed to a three-component vector, which represents a ...
A base class for things that may be directly added into the egg hierarchy.
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
static const LMatrix3d & ident_mat()
Returns an identity matrix.
int compare_to(const LVecBase3d &other) const
This flavor of compare_to uses a default threshold value based on the numeric type.
string to_os_specific() const
Converts the filename from our generic Unix-like convention (forward slashes starting with the root a...
A collection of vertices.
EggVertexPool * get_pool() const
Returns the vertex pool associated with the vertices of the primitive, or NULL if the primitive has n...
static Filename from_os_specific(const string &os_specific, Type type=T_general)
This named constructor returns a Panda-style filename (that is, using forward slashes, and no drive letter) based on the supplied filename string that describes a filename in the local system conventions (for instance, on Windows, it may use backslashes or begin with a drive letter and a colon).
size_t add_hash(size_t hash) const
Adds the vector into the running hash.