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);
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;
471 result->_node->SetName(TSTR(name.c_str()));
472 _mesh_tab[pool] = result;
477 int MaxEggMesh::AddFace(
int v0,
int v1,
int v2,
int tv0,
int tv1,
int tv2,
int cv0,
int cv1,
int cv2,
int tex)
480 if (_face_count == _mesh->numFaces) {
481 int nsize = _face_count*2 + 100;
482 BOOL keep = _mesh->numFaces ? TRUE : FALSE;
483 _mesh->setNumFaces(nsize, keep);
484 _mesh->setNumTVFaces(nsize, keep, _face_count);
485 _mesh->setNumVCFaces(nsize, keep, _face_count);
487 int idx = _face_count++;
488 _mesh->faces[idx].setVerts(v0,v1,v2);
489 _mesh->faces[idx].smGroup = 1;
490 _mesh->faces[idx].flags = EDGE_ALL | HAS_TVERTS;
491 _mesh->faces[idx].setMatID(tex);
492 _mesh->tvFace[idx].setTVerts(tv0,tv1,tv2);
493 _mesh->vcFace[idx].setTVerts(cv0,cv1,cv2);
497 EggGroup *MaxEggMesh::GetControlJoint(
void)
500 VertTable::const_iterator vert = _vert_tab.begin();
501 if (vert == _vert_tab.end())
return 0;
502 switch (vert->_weights.size()) {
504 for (++vert; vert != _vert_tab.end(); ++vert)
505 if (vert->_weights.size() != 0)
506 return CTRLJOINT_DEFORM;
509 result = vert->_weights[0].second;
510 for (++vert; vert != _vert_tab.end(); ++vert)
511 if ((vert->_weights.size() != 1) || (vert->_weights[0].second != result))
512 return CTRLJOINT_DEFORM;
515 return CTRLJOINT_DEFORM;
519 void MaxEggLoader::CreateSkinModifier(
MaxEggMesh *M)
521 vector <MaxEggJoint *> joints;
523 M->_dobj = CreateDerivedObject(M->_obj);
524 M->_node->SetObjectRef(M->_dobj);
525 M->_skin_mod = (Modifier*)CreateInstance(OSM_CLASS_ID, SKIN_CLASSID);
526 M->_iskin = (ISkin*)M->_skin_mod->GetInterface(I_SKIN);
527 M->_iskin_import = (ISkinImportData*)M->_skin_mod->GetInterface(I_SKINIMPORTDATA);
528 M->_dobj->SetAFlag(A_LOCK_TARGET);
529 M->_dobj->AddModifier(M->_skin_mod);
530 M->_dobj->ClearAFlag(A_LOCK_TARGET);
531 GetCOREInterface()->ForceCompleteRedraw();
533 VertTable::const_iterator vert;
534 for (vert=M->_vert_tab.begin(); vert != M->_vert_tab.end(); ++vert) {
535 for (
int i=0; i<vert->_weights.size(); i++) {
536 double strength = vert->_weights[i].first;
537 MaxEggJoint *joint = FindJoint(vert->_weights[i].second);
538 if (!joint->_inskin) {
539 joint->_inskin =
true;
540 joints.push_back(joint);
544 for (
int i=0; i<joints.size(); i++) {
545 BOOL last = (i == (joints.size()-1)) ? TRUE : FALSE;
546 M->_iskin_import->AddBoneEx(joints[i]->_node, last);
547 joints[i]->_inskin =
false;
550 GetCOREInterface()->SetCommandPanelTaskMode(TASK_MODE_MODIFY);
551 GetCOREInterface()->SelectNode(M->_node);
552 GetCOREInterface()->ForceCompleteRedraw();
554 for (vert=M->_vert_tab.begin(); vert != M->_vert_tab.end(); ++vert) {
555 Tab<INode*> maxJoints;
556 Tab<PN_stdfloat> maxWeights;
557 maxJoints.ZeroCount();
558 maxWeights.ZeroCount();
559 for (
int i=0; i<vert->_weights.size(); i++) {
560 PN_stdfloat strength = (PN_stdfloat)(vert->_weights[i].first);
561 MaxEggJoint *joint = FindJoint(vert->_weights[i].second);
562 maxWeights.Append(1,&strength);
563 maxJoints.Append(1,&(joint->_node));
565 M->_iskin_import->AddWeights(M->_node, vert->_index, maxJoints, maxWeights);
580 vector<int> vertIndices;
581 vector<int> tvertIndices;
582 vector<int> cvertIndices;
584 if (node->
is_of_type(EggPolygon::get_class_type())) {
598 EggPolygon::const_iterator ci;
601 tvertIndices.clear();
602 cvertIndices.clear();
603 for (ci = poly->begin(); ci != poly->end(); ++ci) {
607 vertIndices.push_back(mesh->GetVert(vtx, context));
608 tvertIndices.push_back(mesh->GetTVert(uv * uvtrans));
609 cvertIndices.push_back(mesh->GetCVert(vtx->
get_color()));
611 for (
int i=1; i<vertIndices.size()-1; i++)
612 mesh->AddFace(vertIndices[0], vertIndices[i], vertIndices[i+1],
613 tvertIndices[0], tvertIndices[i], tvertIndices[i+1],
614 cvertIndices[0], cvertIndices[i], cvertIndices[i+1],
616 }
else if (node->
is_of_type(EggGroupNode::get_class_type())) {
618 if (node->
is_of_type(EggGroup::get_class_type())) {
621 MakeJoint(group, context);
625 EggGroupNode::const_iterator ci;
626 for (ci = group->begin(); ci != group->end(); ++ci) {
627 TraverseEggNode(*ci, context);
632 bool MaxEggLoader::ConvertEggData(
EggData *data,
bool merge,
bool model,
bool anim)
635 maxloader_cat.error() <<
"Currently, only 'merge' mode is implemented.\n";
639 if ((anim) || (!model)) {
640 maxloader_cat.error() <<
"Currently, only model-loading is implemented.\n";
655 TraverseEggNode(data, NULL);
657 for (ci = _mesh_tab.begin(); ci != _mesh_tab.end(); ++ci) {
659 mesh->_mesh->setNumVerts(mesh->_vert_count, TRUE);
660 mesh->_mesh->setNumTVerts(mesh->_tvert_count, TRUE);
661 mesh->_mesh->setNumVertCol(mesh->_cvert_count, TRUE);
662 mesh->_mesh->setNumFaces(mesh->_face_count, TRUE);
663 mesh->_mesh->setNumTVFaces(mesh->_face_count, TRUE, mesh->_face_count);
664 mesh->_mesh->setNumVCFaces(mesh->_face_count, TRUE, mesh->_face_count);
665 mesh->_mesh->InvalidateTopologyCache();
666 mesh->_mesh->InvalidateGeomCache();
667 mesh->_mesh->buildNormals();
670 double thickness = 0.0;
671 for (ji = _joint_tab.begin(); ji != _joint_tab.end(); ++ji) {
672 double dfo = ((*ji)->GetPos()).length();
673 if (dfo > thickness) thickness = dfo;
675 thickness = thickness * 0.025;
676 for (ji = _joint_tab.begin(); ji != _joint_tab.end(); ++ji) {
678 joint->ChooseEndPos(thickness);
679 joint->CreateMaxBone();
682 for (ci = _mesh_tab.begin(); ci != _mesh_tab.end(); ++ci) {
684 EggGroup *joint = mesh->GetControlJoint();
685 if (joint) CreateSkinModifier(mesh);
690 MultiMtl *mtl = NewDefaultMultiMtl();
691 mtl->SetNumSubMtls(_next_tex);
692 for (ti = _tex_tab.begin(); ti != _tex_tab.end(); ++ti) {
694 mtl->SetSubMtlAndName(tex->_id, tex->_mat, name);
696 for (ci = _mesh_tab.begin(); ci != _mesh_tab.end(); ++ci) {
698 mesh->_node->SetMtl(mtl);
702 for (ci = _mesh_tab.begin(); ci != _mesh_tab.end(); ++ci)
delete *ci;
703 for (ji = _joint_tab.begin(); ji != _joint_tab.end(); ++ji)
delete *ji;
704 for (ti = _tex_tab.begin(); ti != _tex_tab.end(); ++ti)
delete *ti;
709 maxloader_cat.info() <<
"Egg import successful\n";
713 bool MaxEggLoader::ConvertEggFile(
const char *name,
bool merge,
bool model,
bool anim)
717 if (!data.
read(datafn)) {
718 maxloader_cat.error() <<
"Cannot read Egg file for import\n";
721 return ConvertEggData(&data, merge, model, anim);
730 bool MaxLoadEggData(
EggData *data,
bool merge,
bool model,
bool anim)
733 return loader.ConvertEggData(data, merge, model, anim);
736 bool MaxLoadEggFile(
const char *name,
bool merge,
bool model,
bool anim)
739 return loader.ConvertEggFile(name, merge, model, anim);
This is an iterator adaptor that converts any iterator that returns a pair (e.g.
LTexCoordd get_uv() const
Returns the unnamed UV coordinate pair on the vertex.
This is a 4-by-4 transform matrix.
LColor get_color() const
Returns the color set on this particular attribute.
size_t get_hash() const
Returns a suitable hash for phash_map.
A base class for nodes in the hierarchy that are not leaf nodes.
virtual bool is_joint() const
Returns true if this particular node represents a <Joint> entry or not.
Defines a texture map that may be applied to geometry.
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
LVertexd get_pos3() const
Valid if get_num_dimensions() returns 3 or 4.
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 a two-component point in space.
EggTexture * get_texture() const
Returns the first texture on the primitive, if any, or NULL if there are no textures on the primitive...
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...
This is the primary interface into all the egg data, and the root of the egg file structure...
void set_coordinate_system(CoordinateSystem coordsys)
Changes the coordinate system of the EggData.
wstring to_os_specific_w() const
The wide-string variant on to_os_specific().
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...
const Filename & get_fullpath() const
Returns the full pathname to the file, if it is known; otherwise, returns the same thing as get_filen...
bool read(Filename filename, string display_name=string())
Opens the indicated filename and reads the egg data contents from it.
double length() const
Returns the length of the vector, by the Pythagorean theorem.
double get_vertex_membership(const EggVertex *vert) const
Returns the amount of membership of the indicated vertex in this group.
string to_os_specific() const
Converts the filename from our generic Unix-like convention (forward slashes starting with the root a...
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 ...
size_t add_hash(size_t hash) const
Adds the vector into the running hash.
int compare_to(const LVecBase3d &other) const
This flavor of compare_to uses a default threshold value based on the numeric type.
A base class for things that may be directly added into the egg hierarchy.
static const LMatrix3d & ident_mat()
Returns an identity matrix.
LVecBase3d get_row3(int row) const
Retrieves the row column of the matrix as a 3-component vector, ignoring the last column...
A collection of vertices.
bool has_texture() const
Returns true if the primitive has any textures specified, false otherwise.
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).