复制粘贴改进
This commit is contained in:
parent
a30ddbc30e
commit
04afb5ca4b
3
.idea/EG.iml
generated
3
.idea/EG.iml
generated
@ -3,8 +3,9 @@
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/.venv" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/venv" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="Python 3.10 (EG)" jdkType="Python SDK" />
|
||||
<orderEntry type="jdk" jdkName="Python 3.12 virtualenv at ~/EG/venv" jdkType="Python SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
<component name="PyDocumentationSettings">
|
||||
|
||||
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
@ -3,5 +3,5 @@
|
||||
<component name="Black">
|
||||
<option name="sdkName" value="Python 3.12 (PythonProject)" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10 (EG)" project-jdk-type="Python SDK" />
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.12 virtualenv at ~/EG/venv" project-jdk-type="Python SDK" />
|
||||
</project>
|
||||
File diff suppressed because one or more lines are too long
@ -1,53 +0,0 @@
|
||||
if(NOT BUILD_PANDA)
|
||||
message(FATAL_ERROR "Cannot build pandatool without panda! Please enable the BUILD_PANDA option.")
|
||||
endif()
|
||||
|
||||
# Include pandatool source directories
|
||||
add_subdirectory(src/assimp)
|
||||
add_subdirectory(src/bam)
|
||||
add_subdirectory(src/converter)
|
||||
add_subdirectory(src/daeegg)
|
||||
add_subdirectory(src/daeprogs)
|
||||
add_subdirectory(src/deploy-stub)
|
||||
add_subdirectory(src/dxf)
|
||||
add_subdirectory(src/dxfegg)
|
||||
add_subdirectory(src/dxfprogs)
|
||||
add_subdirectory(src/eggbase)
|
||||
add_subdirectory(src/eggcharbase)
|
||||
add_subdirectory(src/eggprogs)
|
||||
add_subdirectory(src/egg-mkfont)
|
||||
add_subdirectory(src/egg-optchar)
|
||||
add_subdirectory(src/egg-palettize)
|
||||
add_subdirectory(src/egg-qtess)
|
||||
add_subdirectory(src/flt)
|
||||
add_subdirectory(src/fltegg)
|
||||
add_subdirectory(src/fltprogs)
|
||||
add_subdirectory(src/gtk-stats)
|
||||
add_subdirectory(src/imagebase)
|
||||
add_subdirectory(src/imageprogs)
|
||||
add_subdirectory(src/lwo)
|
||||
add_subdirectory(src/lwoegg)
|
||||
add_subdirectory(src/lwoprogs)
|
||||
add_subdirectory(src/mac-stats)
|
||||
add_subdirectory(src/miscprogs)
|
||||
add_subdirectory(src/objegg)
|
||||
add_subdirectory(src/objprogs)
|
||||
add_subdirectory(src/palettizer)
|
||||
add_subdirectory(src/pandatoolbase)
|
||||
add_subdirectory(src/pfmprogs)
|
||||
add_subdirectory(src/progbase)
|
||||
add_subdirectory(src/pstatserver)
|
||||
add_subdirectory(src/ptloader)
|
||||
add_subdirectory(src/text-stats)
|
||||
add_subdirectory(src/vrml)
|
||||
add_subdirectory(src/vrmlegg)
|
||||
add_subdirectory(src/vrmlprogs)
|
||||
add_subdirectory(src/win-stats)
|
||||
add_subdirectory(src/xfile)
|
||||
add_subdirectory(src/xfileegg)
|
||||
add_subdirectory(src/xfileprogs)
|
||||
|
||||
if(BUILD_TOOLS)
|
||||
export_targets(Tools)
|
||||
endif()
|
||||
export_targets(ToolsDevel NAMESPACE "Panda3D::Tools::" COMPONENT ToolsDevel)
|
||||
@ -1,39 +0,0 @@
|
||||
if(NOT HAVE_ASSIMP)
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(P3ASSIMP_HEADERS
|
||||
assimpLoader.h assimpLoader.I
|
||||
config_assimp.h
|
||||
loaderFileTypeAssimp.h
|
||||
pandaIOStream.h
|
||||
pandaIOSystem.h
|
||||
pandaLogger.h
|
||||
)
|
||||
|
||||
set(P3ASSIMP_SOURCES
|
||||
assimpLoader.cxx
|
||||
config_assimp.cxx
|
||||
loaderFileTypeAssimp.cxx
|
||||
pandaIOStream.cxx
|
||||
pandaIOSystem.cxx
|
||||
pandaLogger.cxx
|
||||
)
|
||||
|
||||
composite_sources(p3assimp P3ASSIMP_SOURCES)
|
||||
add_library(p3assimp ${MODULE_TYPE} ${P3ASSIMP_HEADERS} ${P3ASSIMP_SOURCES})
|
||||
set_target_properties(p3assimp PROPERTIES DEFINE_SYMBOL BUILDING_ASSIMP)
|
||||
target_link_libraries(p3assimp PRIVATE p3pandatoolbase)
|
||||
target_link_libraries(p3assimp PUBLIC PKG::ASSIMP)
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "^(GNU|Clang)$")
|
||||
# Do not re-export symbols from these libraries.
|
||||
target_link_options(p3assimp PRIVATE "LINKER:--exclude-libs,libassimp.a")
|
||||
target_link_options(p3assimp PRIVATE "LINKER:--exclude-libs,libIrrXML.a")
|
||||
endif()
|
||||
|
||||
if(BUILD_SHARED_LIBS)
|
||||
# We can't install this if we're doing a static build, because it depends on
|
||||
# a static library that isn't installed.
|
||||
install(TARGETS p3assimp EXPORT Assimp COMPONENT Assimp DESTINATION ${MODULE_DESTINATION})
|
||||
endif()
|
||||
@ -1,12 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file assimpLoader.I
|
||||
* @author rdb
|
||||
* @date 2011-03-29
|
||||
*/
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,94 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file assimpLoader.h
|
||||
* @author rdb
|
||||
* @date 2011-03-29
|
||||
*/
|
||||
|
||||
#ifndef ASSIMPLOADER_H
|
||||
#define ASSIMPLOADER_H
|
||||
|
||||
#include "config_assimp.h"
|
||||
#include "filename.h"
|
||||
#include "modelRoot.h"
|
||||
#include "texture.h"
|
||||
#include "textureStage.h"
|
||||
#include "pmap.h"
|
||||
|
||||
#include <assimp/scene.h>
|
||||
#include <assimp/Importer.hpp>
|
||||
|
||||
class Character;
|
||||
class CharacterJointBundle;
|
||||
class PartGroup;
|
||||
class AnimBundle;
|
||||
class AnimGroup;
|
||||
|
||||
struct char_cmp {
|
||||
bool operator () (const char *a, const char *b) const {
|
||||
return strcmp(a,b) < 0;
|
||||
}
|
||||
};
|
||||
typedef pmap<const char *, const aiNode *, char_cmp> BoneMap;
|
||||
|
||||
/**
|
||||
* Class that interfaces with Assimp and builds Panda nodes to represent the
|
||||
* Assimp structures. The loader should be reusable.
|
||||
*/
|
||||
class AssimpLoader : public TypedReferenceCount {
|
||||
public:
|
||||
AssimpLoader();
|
||||
virtual ~AssimpLoader();
|
||||
|
||||
void get_extensions(std::string &ext) const;
|
||||
|
||||
bool read(const Filename &filename);
|
||||
void build_graph();
|
||||
|
||||
public:
|
||||
bool _error;
|
||||
PT(ModelRoot) _root;
|
||||
Filename _filename;
|
||||
Mutex _lock;
|
||||
|
||||
private:
|
||||
Assimp::Importer _importer;
|
||||
const aiScene *_scene;
|
||||
|
||||
struct Geoms {
|
||||
PT(Geom) _points;
|
||||
PT(Geom) _lines;
|
||||
PT(Geom) _triangles;
|
||||
PT(Character) _character;
|
||||
unsigned int _mat_index = 0;
|
||||
};
|
||||
|
||||
// These arrays are temporarily used during the build_graph run.
|
||||
PT(Texture) *_textures;
|
||||
CPT(RenderState) *_mat_states;
|
||||
Geoms *_geoms;
|
||||
BoneMap _bonemap;
|
||||
|
||||
const aiNode *find_node(const aiNode &root, const aiString &name);
|
||||
|
||||
void load_texture(size_t index);
|
||||
void load_texture_stage(const aiMaterial &mat, const aiTextureType &ttype,
|
||||
TextureStage::Mode mode, CPT(TextureAttrib) &tattr,
|
||||
CPT(TexMatrixAttrib) &tmattr);
|
||||
void load_material(size_t index);
|
||||
void create_joint(Character *character, CharacterJointBundle *bundle, PartGroup *parent, const aiNode &node);
|
||||
void create_anim_channel(const aiAnimation &anim, AnimBundle *bundle, AnimGroup *parent, const aiNode &node);
|
||||
void load_mesh(size_t index);
|
||||
bool load_node(const aiNode &node, PandaNode *parent, bool under_joint = false);
|
||||
void load_light(const aiLight &light);
|
||||
};
|
||||
|
||||
#include "assimpLoader.I"
|
||||
|
||||
#endif
|
||||
@ -1,114 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file config_assimp.cxx
|
||||
* @author rdb
|
||||
* @date 2011-03-29
|
||||
*/
|
||||
|
||||
#include "config_assimp.h"
|
||||
|
||||
#include "loaderFileTypeAssimp.h"
|
||||
|
||||
#include "dconfig.h"
|
||||
#include "loaderFileTypeRegistry.h"
|
||||
|
||||
ConfigureDef(config_assimp);
|
||||
NotifyCategoryDef(assimp, "");
|
||||
|
||||
ConfigureFn(config_assimp) {
|
||||
init_libassimp();
|
||||
}
|
||||
|
||||
ConfigVariableBool assimp_calc_tangent_space
|
||||
("assimp-calc-tangent-space", false,
|
||||
PRC_DESC("Calculates tangents and binormals for meshes imported via Assimp."));
|
||||
|
||||
ConfigVariableBool assimp_join_identical_vertices
|
||||
("assimp-join-identical-vertices", true,
|
||||
PRC_DESC("Merges duplicate vertices. Set this to false if you want each "
|
||||
"vertex to only be in use on one triangle."));
|
||||
|
||||
ConfigVariableBool assimp_improve_cache_locality
|
||||
("assimp-improve-cache-locality", true,
|
||||
PRC_DESC("Improves rendering performance of the loaded meshes by reordering "
|
||||
"triangles for better vertex cache locality. Set this to false if "
|
||||
"you need geometry to be loaded in the exact order that it was "
|
||||
"specified in the file, or to improve load performance."));
|
||||
|
||||
ConfigVariableBool assimp_remove_redundant_materials
|
||||
("assimp-remove-redundant-materials", true,
|
||||
PRC_DESC("Removes redundant/unreferenced materials from assets."));
|
||||
|
||||
ConfigVariableBool assimp_fix_infacing_normals
|
||||
("assimp-fix-infacing-normals", false,
|
||||
PRC_DESC("Determines which normal vectors are facing inward and inverts them "
|
||||
"so that they are facing outward."));
|
||||
|
||||
ConfigVariableBool assimp_optimize_meshes
|
||||
("assimp-optimize-meshes", true,
|
||||
PRC_DESC("Reduces the number of draw calls by unifying geometry with the same "
|
||||
"materials. Especially effective in conjunction with "
|
||||
"assimp-optimize-graph and assimp-remove-redundant-materials."));
|
||||
|
||||
ConfigVariableBool assimp_optimize_graph
|
||||
("assimp-optimize-graph", false,
|
||||
PRC_DESC("Optimizes the scene geometry by flattening the scene hierarchy. "
|
||||
"This is very efficient (combined with assimp-optimize-meshes), but "
|
||||
"it may result the hierarchy to become lost, so it is disabled by "
|
||||
"default."));
|
||||
|
||||
ConfigVariableBool assimp_flip_winding_order
|
||||
("assimp-flip-winding-order", false,
|
||||
PRC_DESC("Set this true to flip the winding order of all models loaded via "
|
||||
"the Assimp loader. Note that you may need to clear the model-cache "
|
||||
"after changing this."));
|
||||
|
||||
ConfigVariableBool assimp_gen_normals
|
||||
("assimp-gen-normals", false,
|
||||
PRC_DESC("Set this true to generate normals (if absent from file) on import. "
|
||||
"See assimp-smooth-normal-angle for more information. "
|
||||
"Note that you may need to clear the model-cache after "
|
||||
"changing this."));
|
||||
|
||||
ConfigVariableDouble assimp_smooth_normal_angle
|
||||
("assimp-smooth-normal-angle", 0.0,
|
||||
PRC_DESC("Set this to anything other than 0.0 in degrees (so 180.0 is PI) to "
|
||||
"specify the maximum angle that may be between two face normals at "
|
||||
"the same vertex position that are smoothed together. Sometimes "
|
||||
"referred to as 'crease angle'. Only has effect if "
|
||||
"assimp-gen-normals is set to true and the file does not contain "
|
||||
"normals. Note that you may need to clear the model-cache after "
|
||||
"changing this."));
|
||||
|
||||
ConfigVariableBool assimp_collapse_dummy_root_node
|
||||
("assimp-collapse-dummy-root-node", true,
|
||||
PRC_DESC("If set to true, collapses the root node that Assimp creates, if it "
|
||||
"appears to be a synthetic dummy root node and contains no meshes. "
|
||||
"This variable is new as of Panda3D 1.10.13 and will become true by "
|
||||
"default as of Panda3D 1.11.0."));
|
||||
|
||||
/**
|
||||
* Initializes the library. This must be called at least once before any of
|
||||
* the functions or classes in this library can be used. Normally it will be
|
||||
* called by the static initializers and need not be called explicitly, but
|
||||
* special cases exist.
|
||||
*/
|
||||
void
|
||||
init_libassimp() {
|
||||
static bool initialized = false;
|
||||
if (initialized) {
|
||||
return;
|
||||
}
|
||||
initialized = true;
|
||||
|
||||
LoaderFileTypeAssimp::init_type();
|
||||
|
||||
LoaderFileTypeRegistry *reg = LoaderFileTypeRegistry::get_global_ptr();
|
||||
reg->register_type(new LoaderFileTypeAssimp);
|
||||
}
|
||||
@ -1,40 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file config_assimp.h
|
||||
* @author rdb
|
||||
* @date 2011-03-29
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_ASSIMP_H
|
||||
#define CONFIG_ASSIMP_H
|
||||
|
||||
#include "pandatoolbase.h"
|
||||
#include "notifyCategoryProxy.h"
|
||||
#include "configVariableBool.h"
|
||||
#include "configVariableDouble.h"
|
||||
#include "dconfig.h"
|
||||
|
||||
ConfigureDecl(config_assimp, EXPCL_ASSIMP, EXPTP_ASSIMP);
|
||||
NotifyCategoryDecl(assimp, EXPCL_ASSIMP, EXPTP_ASSIMP);
|
||||
|
||||
extern ConfigVariableBool assimp_calc_tangent_space;
|
||||
extern ConfigVariableBool assimp_join_identical_vertices;
|
||||
extern ConfigVariableBool assimp_improve_cache_locality;
|
||||
extern ConfigVariableBool assimp_remove_redundant_materials;
|
||||
extern ConfigVariableBool assimp_fix_infacing_normals;
|
||||
extern ConfigVariableBool assimp_optimize_meshes;
|
||||
extern ConfigVariableBool assimp_optimize_graph;
|
||||
extern ConfigVariableBool assimp_flip_winding_order;
|
||||
extern ConfigVariableBool assimp_gen_normals;
|
||||
extern ConfigVariableDouble assimp_smooth_normal_angle;
|
||||
extern ConfigVariableBool assimp_collapse_dummy_root_node;
|
||||
|
||||
extern EXPCL_ASSIMP void init_libassimp();
|
||||
|
||||
#endif
|
||||
@ -1,133 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file loaderFileTypeAssimp.cxx
|
||||
* @author rdb
|
||||
* @date 2011-03-29
|
||||
*/
|
||||
|
||||
#include "loaderFileTypeAssimp.h"
|
||||
#include "config_assimp.h"
|
||||
#include "assimpLoader.h"
|
||||
|
||||
#include <assimp/cimport.h>
|
||||
|
||||
using std::string;
|
||||
|
||||
TypeHandle LoaderFileTypeAssimp::_type_handle;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
LoaderFileTypeAssimp::
|
||||
LoaderFileTypeAssimp() {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
LoaderFileTypeAssimp::
|
||||
~LoaderFileTypeAssimp() {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
string LoaderFileTypeAssimp::
|
||||
get_name() const {
|
||||
return "Assimp Importer";
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
string LoaderFileTypeAssimp::
|
||||
get_extension() const {
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a space-separated list of extension, in addition to the one
|
||||
* returned by get_extension(), that are recognized by this converter.
|
||||
*/
|
||||
string LoaderFileTypeAssimp::
|
||||
get_additional_extensions() const {
|
||||
// This may be called at static init time, so ensure it is constructed now.
|
||||
static ConfigVariableString assimp_disable_extensions
|
||||
("assimp-disable-extensions", "gltf glb",
|
||||
PRC_DESC("A list of extensions (without preceding dot) that should not be "
|
||||
"loaded via the Assimp loader, even if Assimp supports these "
|
||||
"formats. It is useful to set this for eg. gltf and glb files "
|
||||
"to prevent them from being accidentally loaded via the Assimp "
|
||||
"plug-in instead of via a superior plug-in like panda3d-gltf."));
|
||||
|
||||
bool has_disabled_exts = !assimp_disable_extensions.empty();
|
||||
|
||||
aiString aexts;
|
||||
aiGetExtensionList(&aexts);
|
||||
|
||||
char *buffer = (char *)alloca(aexts.length + 2);
|
||||
char *p = buffer;
|
||||
|
||||
// The format is like: *.mdc;*.mdl;*.mesh.xml;*.mot
|
||||
char *sub = strtok(aexts.data, ";");
|
||||
while (sub != nullptr) {
|
||||
bool enabled = true;
|
||||
if (has_disabled_exts) {
|
||||
for (size_t i = 0; i < assimp_disable_extensions.get_num_words(); ++i) {
|
||||
std::string disabled_ext = assimp_disable_extensions.get_word(i);
|
||||
if (strcmp(sub + 2, disabled_ext.c_str()) == 0) {
|
||||
enabled = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (enabled) {
|
||||
*(p++) = ' ';
|
||||
size_t len = strlen(sub + 2);
|
||||
memcpy(p, sub + 2, len);
|
||||
p += len;
|
||||
}
|
||||
|
||||
sub = strtok(nullptr, ";");
|
||||
}
|
||||
|
||||
// Strip first space
|
||||
++buffer;
|
||||
return std::string(buffer, p - buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this file type can transparently load compressed files
|
||||
* (with a .pz or .gz extension), false otherwise.
|
||||
*/
|
||||
bool LoaderFileTypeAssimp::
|
||||
supports_compressed() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
PT(PandaNode) LoaderFileTypeAssimp::
|
||||
load_file(const Filename &path, const LoaderOptions &options,
|
||||
BamCacheRecord *record) const {
|
||||
|
||||
assimp_cat.info()
|
||||
<< "Reading " << path << "\n";
|
||||
|
||||
AssimpLoader loader;
|
||||
loader.local_object();
|
||||
|
||||
if (!loader.read(path)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
loader.build_graph();
|
||||
return DCAST(PandaNode, loader._root);
|
||||
}
|
||||
@ -1,57 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file loaderFileTypeAssimp.h
|
||||
* @author rdb
|
||||
* @date 2011-03-29
|
||||
*/
|
||||
|
||||
#ifndef LOADERFILETYPEASSIMP_H
|
||||
#define LOADERFILETYPEASSIMP_H
|
||||
|
||||
#include "config_assimp.h"
|
||||
#include "loaderFileType.h"
|
||||
|
||||
class AssimpLoader;
|
||||
|
||||
/**
|
||||
* This defines the Loader interface that uses the Assimp library to load
|
||||
* various model formats.
|
||||
*/
|
||||
class EXPCL_ASSIMP LoaderFileTypeAssimp : public LoaderFileType {
|
||||
public:
|
||||
LoaderFileTypeAssimp();
|
||||
virtual ~LoaderFileTypeAssimp();
|
||||
|
||||
virtual std::string get_name() const;
|
||||
virtual std::string get_extension() const;
|
||||
virtual std::string get_additional_extensions() const;
|
||||
virtual bool supports_compressed() const;
|
||||
|
||||
virtual PT(PandaNode) load_file(const Filename &path, const LoaderOptions &options,
|
||||
BamCacheRecord *record) const;
|
||||
|
||||
public:
|
||||
static TypeHandle get_class_type() {
|
||||
return _type_handle;
|
||||
}
|
||||
static void init_type() {
|
||||
LoaderFileType::init_type();
|
||||
register_type(_type_handle, "LoaderFileTypeAssimp",
|
||||
LoaderFileType::get_class_type());
|
||||
}
|
||||
virtual TypeHandle get_type() const {
|
||||
return get_class_type();
|
||||
}
|
||||
virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
|
||||
|
||||
private:
|
||||
static TypeHandle _type_handle;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,7 +0,0 @@
|
||||
|
||||
#include "config_assimp.cxx"
|
||||
#include "assimpLoader.cxx"
|
||||
#include "loaderFileTypeAssimp.cxx"
|
||||
#include "pandaIOStream.cxx"
|
||||
#include "pandaIOSystem.cxx"
|
||||
#include "pandaLogger.cxx"
|
||||
@ -1,107 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file pandaIOStream.cxx
|
||||
* @author rdb
|
||||
* @date 2011-03-29
|
||||
*/
|
||||
|
||||
#include "pandaIOStream.h"
|
||||
|
||||
using std::ios;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
PandaIOStream::
|
||||
PandaIOStream(std::istream &stream) : _istream(stream) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the size of this file.
|
||||
*/
|
||||
size_t PandaIOStream::
|
||||
FileSize() const {
|
||||
_istream.clear();
|
||||
std::streampos cur = _istream.tellg();
|
||||
_istream.seekg(0, ios::end);
|
||||
std::streampos end = _istream.tellg();
|
||||
_istream.seekg(cur);
|
||||
return end;
|
||||
}
|
||||
|
||||
/**
|
||||
* See fflush.
|
||||
*/
|
||||
void PandaIOStream::
|
||||
Flush() {
|
||||
nassertv(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* See fread.
|
||||
*/
|
||||
size_t PandaIOStream::
|
||||
Read(void *buffer, size_t size, size_t count) {
|
||||
_istream.read((char *)buffer, size * count);
|
||||
|
||||
if (_istream.eof()) {
|
||||
// Gracefully handle EOF.
|
||||
_istream.clear(ios::eofbit);
|
||||
}
|
||||
|
||||
return _istream.gcount() / size;
|
||||
}
|
||||
|
||||
/**
|
||||
* See fseek.
|
||||
*/
|
||||
aiReturn PandaIOStream::
|
||||
Seek(size_t offset, aiOrigin origin) {
|
||||
switch (origin) {
|
||||
case aiOrigin_SET:
|
||||
_istream.seekg(offset, ios::beg);
|
||||
break;
|
||||
|
||||
case aiOrigin_CUR:
|
||||
_istream.seekg(offset, ios::cur);
|
||||
break;
|
||||
|
||||
case aiOrigin_END:
|
||||
_istream.seekg(offset, ios::end);
|
||||
break;
|
||||
|
||||
default:
|
||||
// Keep compiler happy
|
||||
nassertr(false, AI_FAILURE);
|
||||
break;
|
||||
}
|
||||
|
||||
if (_istream.good()) {
|
||||
return AI_SUCCESS;
|
||||
} else {
|
||||
return AI_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* See ftell.
|
||||
*/
|
||||
size_t PandaIOStream::
|
||||
Tell() const {
|
||||
return _istream.tellg();
|
||||
}
|
||||
|
||||
/**
|
||||
* See fwrite.
|
||||
*/
|
||||
size_t PandaIOStream::
|
||||
Write(const void *buffer, size_t size, size_t count) {
|
||||
nassertr(false, 0);
|
||||
return 0;
|
||||
}
|
||||
@ -1,45 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file pandaIOStream.h
|
||||
* @author rdb
|
||||
* @date 2011-03-29
|
||||
*/
|
||||
|
||||
#ifndef PANDAIOSTREAM_H
|
||||
#define PANDAIOSTREAM_H
|
||||
|
||||
#include "config_assimp.h"
|
||||
|
||||
#include <assimp/IOStream.hpp>
|
||||
|
||||
class PandaIOSystem;
|
||||
|
||||
/**
|
||||
* Custom implementation of Assimp::IOStream. It simply wraps around an
|
||||
* istream object, and is unable to write.
|
||||
*/
|
||||
class PandaIOStream : public Assimp::IOStream {
|
||||
public:
|
||||
PandaIOStream(std::istream &stream);
|
||||
virtual ~PandaIOStream() {};
|
||||
|
||||
size_t FileSize() const;
|
||||
void Flush();
|
||||
size_t Read(void *pvBuffer, size_t pSize, size_t pCount);
|
||||
aiReturn Seek(size_t pOffset, aiOrigin pOrigin);
|
||||
size_t Tell() const;
|
||||
size_t Write(const void *buffer, size_t size, size_t count);
|
||||
|
||||
private:
|
||||
std::istream &_istream;
|
||||
|
||||
friend class PandaIOSystem;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,85 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file pandaIOSystem.cxx
|
||||
* @author rdb
|
||||
* @date 2011-03-29
|
||||
*/
|
||||
|
||||
#include "pandaIOSystem.h"
|
||||
#include "pandaIOStream.h"
|
||||
|
||||
/**
|
||||
* Initializes the object with the given VFS, or the global one if none was
|
||||
* specified.
|
||||
*/
|
||||
PandaIOSystem::
|
||||
PandaIOSystem(VirtualFileSystem *vfs) : _vfs(vfs) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the file exists, duh.
|
||||
*/
|
||||
bool PandaIOSystem::
|
||||
Exists(const char *file) const {
|
||||
Filename fn = Filename::from_os_specific(file);
|
||||
return _vfs->exists(fn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the indicated file stream.
|
||||
*/
|
||||
void PandaIOSystem::
|
||||
Close(Assimp::IOStream *file) {
|
||||
PandaIOStream *pstr = (PandaIOStream*) file;
|
||||
_vfs->close_read_file(&pstr->_istream);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the two paths point to the same file, false if not.
|
||||
*/
|
||||
bool PandaIOSystem::
|
||||
ComparePaths(const char *p1, const char *p2) const {
|
||||
Filename fn1 = Filename::from_os_specific(p1);
|
||||
Filename fn2 = Filename::from_os_specific(p2);
|
||||
fn1.make_canonical();
|
||||
fn2.make_canonical();
|
||||
return fn1 == fn2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path separator for this operating system.
|
||||
*/
|
||||
char PandaIOSystem::
|
||||
getOsSeparator() const {
|
||||
#ifdef _WIN32
|
||||
return '\\';
|
||||
#else
|
||||
return '/';
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the indicated file.
|
||||
*/
|
||||
Assimp::IOStream *PandaIOSystem::
|
||||
Open(const char *file, const char *mode) {
|
||||
Filename fn = Filename::from_os_specific(file);
|
||||
|
||||
if (mode[0] == 'r') {
|
||||
std::istream *stream = _vfs->open_read_file(file, true);
|
||||
if (stream == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
return new PandaIOStream(*stream);
|
||||
|
||||
} else {
|
||||
nassert_raise("write mode not implemented");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
@ -1,40 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file pandaIOSystem.h
|
||||
* @author rdb
|
||||
* @date 2011-03-29
|
||||
*/
|
||||
|
||||
#ifndef PANDAIOSYSTEM_H
|
||||
#define PANDAIOSYSTEM_H
|
||||
|
||||
#include "config_assimp.h"
|
||||
#include "virtualFileSystem.h"
|
||||
|
||||
#include <assimp/IOSystem.hpp>
|
||||
|
||||
/**
|
||||
* Custom implementation of Assimp::IOSystem.
|
||||
*/
|
||||
class PandaIOSystem : public Assimp::IOSystem {
|
||||
public:
|
||||
PandaIOSystem(VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr());
|
||||
virtual ~PandaIOSystem() {};
|
||||
|
||||
void Close(Assimp::IOStream *file);
|
||||
bool ComparePaths(const char *p1, const char *p2) const;
|
||||
bool Exists(const char *file) const;
|
||||
char getOsSeparator() const;
|
||||
Assimp::IOStream *Open(const char *file, const char *mode);
|
||||
|
||||
private:
|
||||
VirtualFileSystem *_vfs;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,71 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file pandaLogger.cxx
|
||||
* @author rdb
|
||||
* @date 2011-05-05
|
||||
*/
|
||||
|
||||
#include "pandaLogger.h"
|
||||
|
||||
#include <assimp/DefaultLogger.hpp>
|
||||
|
||||
PandaLogger *PandaLogger::_ptr = nullptr;
|
||||
|
||||
/**
|
||||
* Makes sure there's a global PandaLogger object and makes sure that it is
|
||||
* Assimp's default logger.
|
||||
*/
|
||||
void PandaLogger::
|
||||
set_default() {
|
||||
if (_ptr == nullptr) {
|
||||
_ptr = new PandaLogger;
|
||||
}
|
||||
if (_ptr != Assimp::DefaultLogger::get()) {
|
||||
Assimp::DefaultLogger::set(_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void PandaLogger::OnDebug(const char *message) {
|
||||
if (assimp_cat.is_debug()) {
|
||||
assimp_cat.debug() << message << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void PandaLogger::OnVerboseDebug(const char *message) {
|
||||
if (assimp_cat.is_spam()) {
|
||||
assimp_cat.spam() << message << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void PandaLogger::OnError(const char *message) {
|
||||
assimp_cat.error() << message << "\n";
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void PandaLogger::OnInfo(const char *message) {
|
||||
assimp_cat.info() << message << "\n";
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void PandaLogger::OnWarn(const char *message) {
|
||||
assimp_cat.warning() << message << "\n";
|
||||
}
|
||||
@ -1,52 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file pandaLogger.h
|
||||
* @author rdb
|
||||
* @date 2011-05-05
|
||||
*/
|
||||
|
||||
#ifndef PANDALOGGER_H
|
||||
#define PANDALOGGER_H
|
||||
|
||||
#include "config_assimp.h"
|
||||
|
||||
#include <assimp/Logger.hpp>
|
||||
|
||||
/**
|
||||
* Custom implementation of Assimp::Logger. It simply wraps around the
|
||||
* assimp_cat methods.
|
||||
*/
|
||||
class PandaLogger : public Assimp::Logger {
|
||||
public:
|
||||
static void set_default();
|
||||
|
||||
protected:
|
||||
INLINE bool attachStream(Assimp::LogStream*, unsigned int) {
|
||||
return false;
|
||||
};
|
||||
INLINE bool detachStream(Assimp::LogStream*, unsigned int) {
|
||||
return false;
|
||||
};
|
||||
|
||||
// Kept for compatibility with Assimp 4.x
|
||||
INLINE bool detatchStream(Assimp::LogStream*, unsigned int) {
|
||||
return false;
|
||||
};
|
||||
|
||||
void OnDebug(const char *message);
|
||||
void OnVerboseDebug(const char *message);
|
||||
void OnError(const char *message);
|
||||
void OnInfo(const char *message);
|
||||
void OnWarn(const char *message);
|
||||
|
||||
private:
|
||||
static PandaLogger *_ptr;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,27 +0,0 @@
|
||||
if(NOT BUILD_TOOLS)
|
||||
return()
|
||||
endif()
|
||||
|
||||
add_executable(bam-info bamInfo.cxx bamInfo.h)
|
||||
target_link_libraries(bam-info p3progbase panda)
|
||||
install(TARGETS bam-info EXPORT Tools COMPONENT Tools DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
|
||||
if(HAVE_EGG)
|
||||
|
||||
add_executable(egg2bam eggToBam.cxx eggToBam.h)
|
||||
target_link_libraries(egg2bam p3eggbase p3progbase panda)
|
||||
install(TARGETS egg2bam EXPORT Tools COMPONENT Tools DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
|
||||
if(HAVE_SQUISH)
|
||||
target_compile_definitions(egg2bam PRIVATE HAVE_SQUISH)
|
||||
endif()
|
||||
|
||||
add_executable(bam2egg bamToEgg.cxx bamToEgg.h)
|
||||
target_link_libraries(bam2egg p3converter p3eggbase p3progbase panda)
|
||||
install(TARGETS bam2egg EXPORT Tools COMPONENT Tools DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
|
||||
add_executable(pts2bam ptsToBam.cxx ptsToBam.h)
|
||||
target_link_libraries(pts2bam p3progbase pandaegg panda)
|
||||
install(TARGETS pts2bam EXPORT Tools COMPONENT Tools DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
|
||||
endif()
|
||||
@ -1,328 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file bamInfo.cxx
|
||||
* @author drose
|
||||
* @date 2000-07-02
|
||||
*/
|
||||
|
||||
#include "bamInfo.h"
|
||||
|
||||
#include "bamFile.h"
|
||||
#include "pandaNode.h"
|
||||
#include "geomNode.h"
|
||||
#include "texture.h"
|
||||
#include "recorderHeader.h"
|
||||
#include "recorderFrame.h"
|
||||
#include "recorderTable.h"
|
||||
#include "dcast.h"
|
||||
#include "pvector.h"
|
||||
#include "bamCacheRecord.h"
|
||||
#include "bamCacheIndex.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
BamInfo::
|
||||
BamInfo() {
|
||||
set_program_brief("describe the contents of .bam files");
|
||||
set_program_description
|
||||
("This program scans one or more Bam files--Panda's Binary Animation "
|
||||
"and Models native binary format--and describes their contents.");
|
||||
|
||||
clear_runlines();
|
||||
add_runline("[opts] input.bam [input.bam ... ]");
|
||||
|
||||
add_option
|
||||
("ls", "", 0,
|
||||
"List the scene graph hierarchy in the bam file.",
|
||||
&BamInfo::dispatch_none, &_ls);
|
||||
|
||||
add_option
|
||||
("t", "", 0,
|
||||
"List explicitly each transition in the hierarchy.",
|
||||
&BamInfo::dispatch_none, &_verbose_transitions);
|
||||
|
||||
add_option
|
||||
("g", "", 0,
|
||||
"Output verbose information about each Geom in the Bam file.",
|
||||
&BamInfo::dispatch_none, &_verbose_geoms);
|
||||
|
||||
_num_scene_graphs = 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void BamInfo::
|
||||
run() {
|
||||
bool okflag = true;
|
||||
|
||||
Filenames::const_iterator fi;
|
||||
for (fi = _filenames.begin(); fi != _filenames.end(); ++fi) {
|
||||
if (!get_info(*fi)) {
|
||||
okflag = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (_num_scene_graphs > 0) {
|
||||
nout << "\nScene graph statistics:\n";
|
||||
_analyzer.write(nout, 2);
|
||||
}
|
||||
nout << "\n";
|
||||
|
||||
if (!okflag) {
|
||||
// Exit with an error if any of the files was unreadable.
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
bool BamInfo::
|
||||
handle_args(ProgramBase::Args &args) {
|
||||
if (args.empty()) {
|
||||
nout << "You must specify the Bam file(s) to read on the command line.\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
ProgramBase::Args::const_iterator ai;
|
||||
for (ai = args.begin(); ai != args.end(); ++ai) {
|
||||
_filenames.push_back(*ai);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reads a single Bam file and displays its contents. Returns true if
|
||||
* successful, false on error.
|
||||
*/
|
||||
bool BamInfo::
|
||||
get_info(const Filename &filename) {
|
||||
BamFile bam_file;
|
||||
|
||||
if (!bam_file.open_read(filename)) {
|
||||
nout << "Unable to read.\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *endian = "little-endian";
|
||||
if (bam_file.get_file_endian() == BamEnums::BE_bigendian) {
|
||||
endian = "big-endian";
|
||||
}
|
||||
int float_width = 32;
|
||||
if (bam_file.get_file_stdfloat_double()) {
|
||||
float_width = 64;
|
||||
}
|
||||
|
||||
nout << filename << " : Bam version " << bam_file.get_file_major_ver()
|
||||
<< "." << bam_file.get_file_minor_ver()
|
||||
<< ", " << endian << ", " << float_width << "-bit floats.\n";
|
||||
|
||||
Objects objects;
|
||||
TypedWritable *object = bam_file.read_object();
|
||||
|
||||
if (object != nullptr &&
|
||||
object->is_exact_type(BamCacheRecord::get_class_type())) {
|
||||
// Here's a special case: if the first object in the file is a
|
||||
// BamCacheRecord, it's a cache data file; in this case, we output the
|
||||
// cache record, and then pretend it doesn't exist.
|
||||
DCAST(BamCacheRecord, object)->write(nout, 2);
|
||||
nout << "\n";
|
||||
object = bam_file.read_object();
|
||||
}
|
||||
|
||||
while (object != nullptr || !bam_file.is_eof()) {
|
||||
if (object != nullptr) {
|
||||
objects.push_back(object);
|
||||
}
|
||||
object = bam_file.read_object();
|
||||
}
|
||||
if (!bam_file.resolve()) {
|
||||
nout << "Unable to fully resolve file.\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// We can't close the bam file until we have examined the objects, since
|
||||
// closing it will decrement reference counts.
|
||||
|
||||
if (objects.size() == 1 &&
|
||||
objects[0]->is_of_type(PandaNode::get_class_type())) {
|
||||
describe_scene_graph(DCAST(PandaNode, objects[0]));
|
||||
|
||||
} else if (objects.size() == 1 &&
|
||||
objects[0]->is_of_type(Texture::get_class_type())) {
|
||||
describe_texture(DCAST(Texture, objects[0]));
|
||||
|
||||
} else if (objects.size() == 1 &&
|
||||
objects[0]->is_of_type(BamCacheIndex::get_class_type())) {
|
||||
describe_cache_index(DCAST(BamCacheIndex, objects[0]));
|
||||
|
||||
} else if (!objects.empty() && objects[0]->is_of_type(RecorderHeader::get_class_type())) {
|
||||
describe_session(DCAST(RecorderHeader, objects[0]), objects);
|
||||
|
||||
} else {
|
||||
nout << "file contains " << objects.size() << " objects:\n";
|
||||
for (int i = 0; i < (int)objects.size(); i++) {
|
||||
describe_general_object(objects[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called for Bam files that contain a single scene graph and no other
|
||||
* objects. This should describe that scene graph in some meaningful way.
|
||||
*/
|
||||
void BamInfo::
|
||||
describe_scene_graph(PandaNode *node) {
|
||||
// Parent the node to our own scene graph root, so we can (a) guarantee it
|
||||
// won't accidentally be deleted before we're done, (b) easily determine the
|
||||
// bounding volume of the scene, and (c) report statistics on all the bam
|
||||
// file's scene graphs together when we've finished.
|
||||
|
||||
PT(PandaNode) root = new PandaNode("root");
|
||||
root->add_child(node);
|
||||
_num_scene_graphs++;
|
||||
|
||||
int num_nodes = _analyzer.get_num_nodes();
|
||||
_analyzer.add_node(node);
|
||||
num_nodes = _analyzer.get_num_nodes() - num_nodes;
|
||||
|
||||
nout << " " << num_nodes << " nodes, bounding volume is "
|
||||
<< *root->get_bounds() << "\n";
|
||||
|
||||
if (_ls || _verbose_geoms || _verbose_transitions) {
|
||||
list_hierarchy(node, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called for Bam files that contain a Texture object.
|
||||
*/
|
||||
void BamInfo::
|
||||
describe_texture(Texture *tex) {
|
||||
tex->write(nout, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called for Bam files that contain a BamCacheIndex object.
|
||||
*/
|
||||
void BamInfo::
|
||||
describe_cache_index(BamCacheIndex *index) {
|
||||
index->write(nout, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called for Bam files that contain a recorded session table.
|
||||
*/
|
||||
void BamInfo::
|
||||
describe_session(RecorderHeader *header, const BamInfo::Objects &objects) {
|
||||
char time_buffer[1024];
|
||||
strftime(time_buffer, 1024, "%c",
|
||||
localtime(&header->_start_time));
|
||||
|
||||
pset<std::string> recorders;
|
||||
double last_timestamp = 0.0;
|
||||
|
||||
for (size_t i = 1; i < objects.size(); i++) {
|
||||
if (objects[i]->is_of_type(RecorderFrame::get_class_type())) {
|
||||
RecorderFrame *frame = DCAST(RecorderFrame, objects[i]);
|
||||
if (frame->_table_changed) {
|
||||
RecorderTable::Recorders::const_iterator ri;
|
||||
for (ri = frame->_table->_recorders.begin();
|
||||
ri != frame->_table->_recorders.end();
|
||||
++ri) {
|
||||
recorders.insert((*ri).first);
|
||||
}
|
||||
}
|
||||
last_timestamp = frame->_timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
nout << "Session, " << last_timestamp
|
||||
<< " secs, " << objects.size() - 1 << " frames, "
|
||||
<< time_buffer << ".\n"
|
||||
<< "Recorders:";
|
||||
for (pset<std::string>::iterator ni = recorders.begin();
|
||||
ni != recorders.end();
|
||||
++ni) {
|
||||
nout << " " << (*ni);
|
||||
}
|
||||
nout << "\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Called for Bam files that contain multiple objects which may or may not be
|
||||
* scene graph nodes. This should describe each object in some meaningful
|
||||
* way.
|
||||
*/
|
||||
void BamInfo::
|
||||
describe_general_object(TypedWritable *object) {
|
||||
nassertv(object != nullptr);
|
||||
nout << " " << object->get_type() << "\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the hierarchy and all of the verbose GeomNode information.
|
||||
*/
|
||||
void BamInfo::
|
||||
list_hierarchy(PandaNode *node, int indent_level) {
|
||||
indent(nout, indent_level) << *node;
|
||||
|
||||
if (_verbose_transitions) {
|
||||
nout << "\n";
|
||||
if (!node->get_transform()->is_identity()) {
|
||||
node->get_transform()->write(nout, indent_level);
|
||||
}
|
||||
if (!node->get_state()->is_empty()) {
|
||||
node->get_state()->write(nout, indent_level);
|
||||
}
|
||||
if (!node->get_effects()->is_empty()) {
|
||||
node->get_effects()->write(nout, indent_level);
|
||||
}
|
||||
|
||||
} else {
|
||||
if (!node->get_transform()->is_identity()) {
|
||||
nout << " " << *node->get_transform();
|
||||
}
|
||||
if (!node->get_state()->is_empty()) {
|
||||
nout << " " << *node->get_state();
|
||||
}
|
||||
if (!node->get_effects()->is_empty()) {
|
||||
nout << " " << *node->get_effects();
|
||||
}
|
||||
nout << "\n";
|
||||
}
|
||||
|
||||
if (_verbose_geoms && node->is_geom_node()) {
|
||||
GeomNode *geom_node;
|
||||
DCAST_INTO_V(geom_node, node);
|
||||
geom_node->write_verbose(nout, indent_level);
|
||||
}
|
||||
|
||||
int num_children = node->get_num_children();
|
||||
for (int i = 0; i < num_children; i++) {
|
||||
PandaNode *child = node->get_child(i);
|
||||
list_hierarchy(child, indent_level + 2);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
BamInfo prog;
|
||||
prog.parse_command_line(argc, argv);
|
||||
prog.run();
|
||||
return 0;
|
||||
}
|
||||
@ -1,65 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file bamInfo.h
|
||||
* @author drose
|
||||
* @date 2000-07-02
|
||||
*/
|
||||
|
||||
#ifndef BAMINFO_H
|
||||
#define BAMINFO_H
|
||||
|
||||
#include "pandatoolbase.h"
|
||||
|
||||
#include "programBase.h"
|
||||
#include "filename.h"
|
||||
#include "sceneGraphAnalyzer.h"
|
||||
|
||||
#include "pvector.h"
|
||||
|
||||
class TypedWritable;
|
||||
class PandaNode;
|
||||
class Texture;
|
||||
class BamCacheIndex;
|
||||
class RecorderHeader;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class BamInfo : public ProgramBase {
|
||||
public:
|
||||
BamInfo();
|
||||
|
||||
void run();
|
||||
|
||||
protected:
|
||||
virtual bool handle_args(Args &args);
|
||||
|
||||
private:
|
||||
typedef pvector<TypedWritable *> Objects;
|
||||
|
||||
bool get_info(const Filename &filename);
|
||||
void describe_scene_graph(PandaNode *node);
|
||||
void describe_texture(Texture *tex);
|
||||
void describe_cache_index(BamCacheIndex *index);
|
||||
void describe_session(RecorderHeader *header, const Objects &objects);
|
||||
void describe_general_object(TypedWritable *object);
|
||||
void list_hierarchy(PandaNode *node, int indent_level);
|
||||
|
||||
typedef pvector<Filename> Filenames;
|
||||
Filenames _filenames;
|
||||
|
||||
bool _ls;
|
||||
bool _verbose_transitions;
|
||||
bool _verbose_geoms;
|
||||
|
||||
int _num_scene_graphs;
|
||||
SceneGraphAnalyzer _analyzer;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,104 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file bamToEgg.cxx
|
||||
* @author drose
|
||||
* @date 2001-06-25
|
||||
*/
|
||||
|
||||
#include "bamToEgg.h"
|
||||
#include "save_egg_file.h"
|
||||
#include "string_utils.h"
|
||||
#include "bamFile.h"
|
||||
#include "bamCacheRecord.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
BamToEgg::
|
||||
BamToEgg() :
|
||||
SomethingToEgg("bam", ".bam")
|
||||
{
|
||||
set_program_brief("convert a native Panda .bam file to an .egg file");
|
||||
set_program_description
|
||||
("This program converts native Panda bam files to egg. The conversion "
|
||||
"is somewhat incomplete; running egg2bam followed by bam2egg should not "
|
||||
"be expected to yield the same egg file you started with.");
|
||||
|
||||
redescribe_option
|
||||
("cs",
|
||||
"Specify the coordinate system of the input " + _format_name +
|
||||
" file. By default, this is taken from the Config.prc file, which "
|
||||
"is currently " + format_string(get_default_coordinate_system()) + ".");
|
||||
|
||||
_coordinate_system = get_default_coordinate_system();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void BamToEgg::
|
||||
run() {
|
||||
BamFile bam_file;
|
||||
|
||||
if (!bam_file.open_read(_input_filename)) {
|
||||
nout << "Unable to read " << _input_filename << "\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
nout << _input_filename << " : Bam version "
|
||||
<< bam_file.get_file_major_ver() << "."
|
||||
<< bam_file.get_file_minor_ver() << "\n";
|
||||
|
||||
typedef pvector<TypedWritable *> Objects;
|
||||
Objects objects;
|
||||
TypedWritable *object = bam_file.read_object();
|
||||
|
||||
if (object != nullptr &&
|
||||
object->is_exact_type(BamCacheRecord::get_class_type())) {
|
||||
// Here's a special case: if the first object in the file is a
|
||||
// BamCacheRecord, it's really a cache data file and not a true bam file;
|
||||
// but skip over the cache data record and let the user treat it like an
|
||||
// ordinary bam file.
|
||||
object = bam_file.read_object();
|
||||
}
|
||||
|
||||
while (object != nullptr || !bam_file.is_eof()) {
|
||||
if (object != nullptr) {
|
||||
ReferenceCount *ref_ptr = object->as_reference_count();
|
||||
if (ref_ptr != nullptr) {
|
||||
ref_ptr->ref();
|
||||
}
|
||||
objects.push_back(object);
|
||||
}
|
||||
object = bam_file.read_object();
|
||||
}
|
||||
bam_file.resolve();
|
||||
bam_file.close();
|
||||
|
||||
_data->set_coordinate_system(_coordinate_system);
|
||||
|
||||
if (objects.size() == 1 &&
|
||||
objects[0]->is_of_type(PandaNode::get_class_type())) {
|
||||
PandaNode *node = DCAST(PandaNode, objects[0]);
|
||||
save_egg_data(_data, node);
|
||||
|
||||
} else {
|
||||
nout << "File does not contain a scene graph.\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
write_egg_file();
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
BamToEgg prog;
|
||||
prog.parse_command_line(argc, argv);
|
||||
prog.run();
|
||||
return 0;
|
||||
}
|
||||
@ -1,34 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file bamToEgg.h
|
||||
* @author drose
|
||||
* @date 2001-06-25
|
||||
*/
|
||||
|
||||
#ifndef BAMTOEGG_H
|
||||
#define BAMTOEGG_H
|
||||
|
||||
#include "pandatoolbase.h"
|
||||
|
||||
#include "somethingToEgg.h"
|
||||
|
||||
/**
|
||||
* This program reads a bam file, for instance as written out from a real-time
|
||||
* interaction session, and generates a corresponding egg file.
|
||||
*/
|
||||
class BamToEgg : public SomethingToEgg {
|
||||
public:
|
||||
BamToEgg();
|
||||
|
||||
void run();
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,491 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file eggToBam.cxx
|
||||
* @author drose
|
||||
* @date 2000-06-28
|
||||
*/
|
||||
|
||||
#include "eggToBam.h"
|
||||
|
||||
#include "config_putil.h"
|
||||
#include "bamFile.h"
|
||||
#include "load_egg_file.h"
|
||||
#include "config_egg2pg.h"
|
||||
#include "config_gobj.h"
|
||||
#include "config_chan.h"
|
||||
#include "pandaNode.h"
|
||||
#include "geomNode.h"
|
||||
#include "renderState.h"
|
||||
#include "textureAttrib.h"
|
||||
#include "dcast.h"
|
||||
#include "graphicsPipeSelection.h"
|
||||
#include "graphicsEngine.h"
|
||||
#include "graphicsBuffer.h"
|
||||
#include "graphicsStateGuardian.h"
|
||||
#include "load_prc_file.h"
|
||||
#include "windowProperties.h"
|
||||
#include "frameBufferProperties.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
EggToBam::
|
||||
EggToBam() :
|
||||
EggToSomething("Bam", ".bam", true, false)
|
||||
{
|
||||
set_program_brief("convert .egg files to .bam files");
|
||||
set_program_description
|
||||
("This program reads Egg files and outputs Bam files, the binary format "
|
||||
"suitable for direct loading of animation and models into Panda. Bam "
|
||||
"files are tied to a particular version of Panda, so should not be "
|
||||
"considered replacements for egg files, but they tend to be smaller and "
|
||||
"load much faster than the equivalent egg files.");
|
||||
|
||||
// -f is always in effect for egg2bam. It doesn't make sense to provide it
|
||||
// as an option to the user.
|
||||
remove_option("f");
|
||||
|
||||
add_path_replace_options();
|
||||
add_path_store_options();
|
||||
|
||||
add_option
|
||||
("flatten", "flag", 0,
|
||||
"Specifies whether to flatten the egg hierarchy after it is loaded. "
|
||||
"If flag is zero, the egg hierarchy will not be flattened, but will "
|
||||
"instead be written to the bam file exactly as it is. If flag is "
|
||||
"non-zero, the hierarchy will be flattened so that unnecessary nodes "
|
||||
"(usually group nodes with only one child) are eliminated. The default "
|
||||
"if this is not specified is taken from the egg-flatten Config.prc "
|
||||
"variable.",
|
||||
&EggToBam::dispatch_int, &_has_egg_flatten, &_egg_flatten);
|
||||
|
||||
add_option
|
||||
("combine-geoms", "flag", 0,
|
||||
"Specifies whether to combine sibling GeomNodes into a common GeomNode "
|
||||
"when possible. This flag is only respected if flatten, above, is also "
|
||||
"enabled (or implicitly true from the Config.prc file). The default if "
|
||||
"this is not specified is taken from the egg-combine-geoms Config.prc "
|
||||
"variable.",
|
||||
&EggToBam::dispatch_int, &_has_egg_combine_geoms, &_egg_combine_geoms);
|
||||
|
||||
add_option
|
||||
("suppress-hidden", "flag", 0,
|
||||
"Specifies whether to suppress hidden geometry. If this is nonzero, "
|
||||
"egg geometry tagged as \"hidden\" will be removed from the final "
|
||||
"scene graph; otherwise, it will be preserved (but stashed). The "
|
||||
"default is nonzero, to remove it.",
|
||||
&EggToBam::dispatch_int, nullptr, &_egg_suppress_hidden);
|
||||
|
||||
add_option
|
||||
("ls", "", 0,
|
||||
"Writes a scene graph listing to standard output after the egg "
|
||||
"file has been loaded, showing the nodes that will be written out.",
|
||||
&EggToBam::dispatch_none, &_ls);
|
||||
|
||||
add_option
|
||||
("C", "quality", 0,
|
||||
"Specify the quality level for lossy channel compression. If this "
|
||||
"is specified, the animation channels will be compressed at this "
|
||||
"quality level, which is normally an integer value between 0 and 100, "
|
||||
"inclusive, where higher numbers produce larger files with greater "
|
||||
"quality. Generally, 95 is the highest useful quality level. Use "
|
||||
"-NC (described below) to disable channel compression. If neither "
|
||||
"option is specified, the default comes from the Config.prc file.",
|
||||
&EggToBam::dispatch_int, &_has_compression_quality, &_compression_quality);
|
||||
|
||||
add_option
|
||||
("NC", "", 0,
|
||||
"Turn off lossy compression of animation channels. Channels will be "
|
||||
"written exactly as they are, losslessly.",
|
||||
&EggToBam::dispatch_none, &_compression_off);
|
||||
|
||||
add_option
|
||||
("rawtex", "", 0,
|
||||
"Record texture data directly in the bam file, instead of storing "
|
||||
"a reference to the texture elsewhere on disk. The textures are "
|
||||
"stored uncompressed, unless -ctex is also specified. "
|
||||
"A particular texture that is encoded into "
|
||||
"multiple different bam files in this way cannot be unified into "
|
||||
"the same part of texture memory if the different bam files are loaded "
|
||||
"together. That being said, this can sometimes be a convenient "
|
||||
"way to ensure the bam file is completely self-contained.",
|
||||
&EggToBam::dispatch_none, &_tex_rawdata);
|
||||
|
||||
add_option
|
||||
("txo", "", 0,
|
||||
"Rather than writing texture data directly into the bam file, as in "
|
||||
"-rawtex, create a texture object for each referenced texture. A "
|
||||
"texture object is a kind of mini-bam file, with a .txo extension, "
|
||||
"that contains all of the data needed to recreate a texture, including "
|
||||
"its image contents, filter and wrap settings, and so on. 3-D textures "
|
||||
"and cube maps can also be represented in a single .txo file. Texture "
|
||||
"object files, like bam files, are tied to a particular version of "
|
||||
"Panda.",
|
||||
&EggToBam::dispatch_none, &_tex_txo);
|
||||
|
||||
#ifdef HAVE_ZLIB
|
||||
add_option
|
||||
("txopz", "", 0,
|
||||
"In addition to writing texture object files as above, compress each "
|
||||
"one using pzip to a .txo.pz file. In many cases, this will yield a "
|
||||
"disk file size comparable to that achieved by png compression. This "
|
||||
"is an on-disk compression only, and does not affect the amount of "
|
||||
"RAM or texture memory consumed by the texture when it is loaded.",
|
||||
&EggToBam::dispatch_none, &_tex_txopz);
|
||||
#endif // HAVE_ZLIB
|
||||
|
||||
add_option
|
||||
("ctex", "", 0,
|
||||
#ifdef HAVE_SQUISH
|
||||
"Pre-compress the texture images using the libsquish library, when "
|
||||
"using -rawtex or -txo. "
|
||||
#else
|
||||
"Asks the graphics card to pre-compress the texture images when using "
|
||||
"-rawtex or -txo. "
|
||||
#endif // HAVE_SQUISH
|
||||
#ifdef HAVE_ZLIB
|
||||
"This is unrelated to the on-disk compression achieved "
|
||||
"via -txopz (and it may be used in conjunction with that parameter). "
|
||||
#endif // HAVE_ZLIB
|
||||
"This will result in a smaller RAM and texture memory footprint for "
|
||||
"the texture images. The same "
|
||||
"effect can be achieved at load time by setting compressed-textures in "
|
||||
"your Config.prc file; but -ctex pre-compresses the "
|
||||
"textures so that they do not need to be compressed at load time. "
|
||||
#ifndef HAVE_SQUISH
|
||||
"Note that, since your Panda is not compiled with the libsquish "
|
||||
"library, using -ctex will make .txo files that are only guaranteed "
|
||||
"to load on the particular graphics card that was used to "
|
||||
"generate them."
|
||||
#endif // HAVE_SQUISH
|
||||
,
|
||||
&EggToBam::dispatch_none, &_tex_ctex);
|
||||
|
||||
add_option
|
||||
("mipmap", "", 0,
|
||||
"Records the pre-generated mipmap levels in the texture object file "
|
||||
"when using -rawtex or -txo, regardless of the texture filter mode. This "
|
||||
"will increase the size of the texture object file by about 33%, but "
|
||||
"it prevents the need to compute the mipmaps at runtime. The default "
|
||||
"is to record mipmap levels only when the texture uses a mipmap "
|
||||
"filter mode.",
|
||||
&EggToBam::dispatch_none, &_tex_mipmap);
|
||||
|
||||
add_option
|
||||
("ctexq", "quality", 0,
|
||||
"Specifies the compression quality to use when performing the "
|
||||
"texture compression requested by -ctex. This may be one of "
|
||||
"'default', 'fastest', 'normal', or 'best'. The default is 'best'. "
|
||||
"Set it to 'default' to use whatever is specified by the Config.prc "
|
||||
"file. This is a global setting only; individual texture quality "
|
||||
"settings appearing within the egg file will override this.",
|
||||
&EggToBam::dispatch_string, nullptr, &_ctex_quality);
|
||||
|
||||
add_option
|
||||
("load-display", "display name", 0,
|
||||
"Specifies the particular display module to load to perform the texture "
|
||||
"compression requested by -ctex. If this is omitted, the default is "
|
||||
"taken from the Config.prc file."
|
||||
#ifdef HAVE_SQUISH
|
||||
" Since your Panda has libsquish compiled in, this is not necessary; "
|
||||
"Panda can compress textures without loading a display module."
|
||||
#endif // HAVE_SQUISH
|
||||
,
|
||||
&EggToBam::dispatch_string, nullptr, &_load_display);
|
||||
|
||||
redescribe_option
|
||||
("cs",
|
||||
"Specify the coordinate system of the resulting " + _format_name +
|
||||
" file. This may be "
|
||||
"one of 'y-up', 'z-up', 'y-up-left', or 'z-up-left'. The default "
|
||||
"is z-up.");
|
||||
|
||||
_force_complete = true;
|
||||
_egg_flatten = 0;
|
||||
_egg_combine_geoms = 0;
|
||||
_egg_suppress_hidden = 1;
|
||||
_tex_txopz = false;
|
||||
_ctex_quality = "best";
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void EggToBam::
|
||||
run() {
|
||||
if (_has_egg_flatten) {
|
||||
// If the user specified some -flatten, we need to set the corresponding
|
||||
// Config.prc variable.
|
||||
egg_flatten = (_egg_flatten != 0);
|
||||
}
|
||||
if (_has_egg_combine_geoms) {
|
||||
// Ditto with -combine_geoms.
|
||||
egg_combine_geoms = (_egg_combine_geoms != 0);
|
||||
}
|
||||
|
||||
// We always set egg_suppress_hidden.
|
||||
egg_suppress_hidden = _egg_suppress_hidden;
|
||||
|
||||
if (_compression_off) {
|
||||
// If the user specified -NC, turn off channel compression.
|
||||
compress_channels = false;
|
||||
|
||||
} else if (_has_compression_quality) {
|
||||
// Otherwise, if the user specified a compression quality with -C, use
|
||||
// that quality level.
|
||||
compress_channels = true;
|
||||
compress_chan_quality = _compression_quality;
|
||||
}
|
||||
|
||||
if (_ctex_quality != "default") {
|
||||
// Override the user's config file with the command-line parameter for
|
||||
// texture compression.
|
||||
std::string prc = "texture-quality-level " + _ctex_quality;
|
||||
load_prc_file_data("prc", prc);
|
||||
}
|
||||
|
||||
if (!_got_coordinate_system) {
|
||||
// If the user didn't specify otherwise, ensure the coordinate system is
|
||||
// Z-up.
|
||||
_data->set_coordinate_system(CS_zup_right);
|
||||
}
|
||||
|
||||
PT(PandaNode) root = load_egg_data(_data);
|
||||
if (root == nullptr) {
|
||||
nout << "Unable to build scene graph from egg file.\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (_tex_ctex) {
|
||||
#ifndef HAVE_SQUISH
|
||||
if (!make_buffer()) {
|
||||
nout << "Unable to initialize graphics context; cannot compress textures.\n";
|
||||
exit(1);
|
||||
}
|
||||
#endif // HAVE_SQUISH
|
||||
}
|
||||
|
||||
if (_tex_txo || _tex_txopz || (_tex_ctex && _tex_rawdata)) {
|
||||
collect_textures(root);
|
||||
Textures::iterator ti;
|
||||
for (ti = _textures.begin(); ti != _textures.end(); ++ti) {
|
||||
Texture *tex = (*ti);
|
||||
tex->get_ram_image();
|
||||
bool want_mipmaps = (_tex_mipmap || tex->uses_mipmaps());
|
||||
if (want_mipmaps) {
|
||||
// Generate mipmap levels.
|
||||
tex->generate_ram_mipmap_images();
|
||||
}
|
||||
|
||||
if (_tex_ctex) {
|
||||
#ifdef HAVE_SQUISH
|
||||
if (!tex->compress_ram_image()) {
|
||||
nout << " couldn't compress " << tex->get_name() << "\n";
|
||||
}
|
||||
tex->set_compression(Texture::CM_on);
|
||||
#else // HAVE_SQUISH
|
||||
tex->set_keep_ram_image(true);
|
||||
bool has_mipmap_levels = (tex->get_num_ram_mipmap_images() > 1);
|
||||
if (!_engine->extract_texture_data(tex, _gsg)) {
|
||||
nout << " couldn't compress " << tex->get_name() << "\n";
|
||||
}
|
||||
if (!has_mipmap_levels && !want_mipmaps) {
|
||||
// Make sure we didn't accidentally introduce mipmap levels by
|
||||
// rendezvousing through the graphics card.
|
||||
tex->clear_ram_mipmap_images();
|
||||
}
|
||||
tex->set_keep_ram_image(false);
|
||||
#endif // HAVE_SQUISH
|
||||
}
|
||||
|
||||
if (_tex_txo || _tex_txopz) {
|
||||
convert_txo(tex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_ls) {
|
||||
root->ls(nout, 0);
|
||||
}
|
||||
|
||||
// This should be guaranteed because we pass false to the constructor,
|
||||
// above.
|
||||
nassertv(has_output_filename());
|
||||
|
||||
Filename filename = get_output_filename();
|
||||
filename.make_dir();
|
||||
nout << "Writing " << filename << "\n";
|
||||
BamFile bam_file;
|
||||
if (!bam_file.open_write(filename)) {
|
||||
nout << "Error in writing.\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!bam_file.write_object(root)) {
|
||||
nout << "Error in writing.\n";
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Does something with the additional arguments on the command line (after all
|
||||
* the -options have been parsed). Returns true if the arguments are good,
|
||||
* false otherwise.
|
||||
*/
|
||||
bool EggToBam::
|
||||
handle_args(ProgramBase::Args &args) {
|
||||
// If the user specified a path store option, we need to set the bam-
|
||||
// texture-mode Config.prc variable directly to support this (otherwise the
|
||||
// bam code will do what it wants to do anyway).
|
||||
if (_tex_rawdata) {
|
||||
bam_texture_mode = BamFile::BTM_rawdata;
|
||||
|
||||
} else if (_got_path_store) {
|
||||
bam_texture_mode = BamFile::BTM_unchanged;
|
||||
|
||||
} else {
|
||||
// Otherwise, the default path store is absolute; then the bam-texture-
|
||||
// mode can do the appropriate thing to it.
|
||||
_path_replace->_path_store = PS_absolute;
|
||||
}
|
||||
|
||||
return EggToSomething::handle_args(args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively walks the scene graph, looking for Texture references.
|
||||
*/
|
||||
void EggToBam::
|
||||
collect_textures(PandaNode *node) {
|
||||
collect_textures(node->get_state());
|
||||
if (node->is_geom_node()) {
|
||||
GeomNode *geom_node = DCAST(GeomNode, node);
|
||||
int num_geoms = geom_node->get_num_geoms();
|
||||
for (int i = 0; i < num_geoms; ++i) {
|
||||
collect_textures(geom_node->get_geom_state(i));
|
||||
}
|
||||
}
|
||||
|
||||
PandaNode::Children children = node->get_children();
|
||||
int num_children = children.get_num_children();
|
||||
for (int i = 0; i < num_children; ++i) {
|
||||
collect_textures(children.get_child(i));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively walks the scene graph, looking for Texture references.
|
||||
*/
|
||||
void EggToBam::
|
||||
collect_textures(const RenderState *state) {
|
||||
const TextureAttrib *tex_attrib = DCAST(TextureAttrib, state->get_attrib(TextureAttrib::get_class_type()));
|
||||
if (tex_attrib != nullptr) {
|
||||
int num_on_stages = tex_attrib->get_num_on_stages();
|
||||
for (int i = 0; i < num_on_stages; ++i) {
|
||||
_textures.insert(tex_attrib->get_on_texture(tex_attrib->get_on_stage(i)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the indicated Texture was not already loaded from a txo file, writes it
|
||||
* to a txo file and updates the Texture object to reference the new file.
|
||||
*/
|
||||
void EggToBam::
|
||||
convert_txo(Texture *tex) {
|
||||
if (!tex->get_loaded_from_txo()) {
|
||||
Filename fullpath = tex->get_fullpath().get_filename_index(0);
|
||||
if (_tex_txopz) {
|
||||
fullpath.set_extension("txo.pz");
|
||||
// We use this clumsy syntax so that the new extension appears to be two
|
||||
// separate extensions, .txo followed by .pz, which is what
|
||||
// Texture::write() expects to find.
|
||||
fullpath = Filename(fullpath.get_fullpath());
|
||||
} else {
|
||||
fullpath.set_extension("txo");
|
||||
}
|
||||
|
||||
if (tex->write(fullpath)) {
|
||||
nout << " Writing " << fullpath;
|
||||
if (tex->get_ram_image_compression() != Texture::CM_off) {
|
||||
nout << " (compressed " << tex->get_ram_image_compression() << ")";
|
||||
}
|
||||
nout << "\n";
|
||||
tex->set_loaded_from_txo();
|
||||
tex->set_fullpath(fullpath);
|
||||
tex->clear_alpha_fullpath();
|
||||
|
||||
Filename filename = tex->get_filename().get_filename_index(0);
|
||||
if (_tex_txopz) {
|
||||
filename.set_extension("txo.pz");
|
||||
filename = Filename(filename.get_fullpath());
|
||||
} else {
|
||||
filename.set_extension("txo");
|
||||
}
|
||||
|
||||
tex->set_filename(filename);
|
||||
tex->clear_alpha_filename();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a GraphicsBuffer for communicating with the graphics card.
|
||||
*/
|
||||
bool EggToBam::
|
||||
make_buffer() {
|
||||
if (!_load_display.empty()) {
|
||||
// Override the user's config file with the command-line parameter.
|
||||
std::string prc = "load-display " + _load_display;
|
||||
load_prc_file_data("prc", prc);
|
||||
}
|
||||
|
||||
GraphicsPipeSelection *selection = GraphicsPipeSelection::get_global_ptr();
|
||||
_pipe = selection->make_default_pipe();
|
||||
if (_pipe == nullptr) {
|
||||
nout << "Unable to create graphics pipe.\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
_engine = new GraphicsEngine;
|
||||
|
||||
FrameBufferProperties fbprops = FrameBufferProperties::get_default();
|
||||
|
||||
// Some graphics drivers can only create single-buffered offscreen buffers.
|
||||
// So request that.
|
||||
fbprops.set_back_buffers(0);
|
||||
|
||||
WindowProperties winprops;
|
||||
winprops.set_size(1, 1);
|
||||
winprops.set_origin(0, 0);
|
||||
winprops.set_undecorated(true);
|
||||
winprops.set_open(true);
|
||||
winprops.set_z_order(WindowProperties::Z_bottom);
|
||||
|
||||
// We don't care how big the buffer is; we just need it to manifest the GSG.
|
||||
_buffer = _engine->make_output(_pipe, "buffer", 0,
|
||||
fbprops, winprops,
|
||||
GraphicsPipe::BF_fb_props_optional);
|
||||
_engine->open_windows();
|
||||
if (_buffer == nullptr || !_buffer->is_valid()) {
|
||||
nout << "Unable to create graphics window.\n";
|
||||
return false;
|
||||
}
|
||||
_gsg = _buffer->get_gsg();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
EggToBam prog;
|
||||
prog.parse_command_line(argc, argv);
|
||||
prog.run();
|
||||
return 0;
|
||||
}
|
||||
@ -1,77 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file eggToBam.h
|
||||
* @author drose
|
||||
* @date 2000-06-28
|
||||
*/
|
||||
|
||||
#ifndef EGGTOBAM_H
|
||||
#define EGGTOBAM_H
|
||||
|
||||
#include "pandatoolbase.h"
|
||||
|
||||
#include "eggToSomething.h"
|
||||
#include "pset.h"
|
||||
#include "graphicsPipe.h"
|
||||
|
||||
class PandaNode;
|
||||
class RenderState;
|
||||
class Texture;
|
||||
class GraphicsEngine;
|
||||
class GraphicsStateGuardian;
|
||||
class GraphicsOutput;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class EggToBam : public EggToSomething {
|
||||
public:
|
||||
EggToBam();
|
||||
|
||||
void run();
|
||||
|
||||
protected:
|
||||
virtual bool handle_args(Args &args);
|
||||
|
||||
private:
|
||||
void collect_textures(PandaNode *node);
|
||||
void collect_textures(const RenderState *state);
|
||||
void convert_txo(Texture *tex);
|
||||
|
||||
bool make_buffer();
|
||||
|
||||
private:
|
||||
typedef pset<Texture *> Textures;
|
||||
Textures _textures;
|
||||
|
||||
bool _has_egg_flatten;
|
||||
int _egg_flatten;
|
||||
bool _has_egg_combine_geoms;
|
||||
int _egg_combine_geoms;
|
||||
bool _egg_suppress_hidden;
|
||||
bool _ls;
|
||||
bool _has_compression_quality;
|
||||
int _compression_quality;
|
||||
bool _compression_off;
|
||||
bool _tex_rawdata;
|
||||
bool _tex_txo;
|
||||
bool _tex_txopz;
|
||||
bool _tex_ctex;
|
||||
bool _tex_mipmap;
|
||||
std::string _ctex_quality;
|
||||
std::string _load_display;
|
||||
|
||||
// The rest of this is required to support -ctex.
|
||||
PT(GraphicsPipe) _pipe;
|
||||
GraphicsStateGuardian *_gsg;
|
||||
GraphicsEngine *_engine;
|
||||
GraphicsOutput *_buffer;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,238 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file ptsToBam.cxx
|
||||
* @author drose
|
||||
* @date 2000-06-28
|
||||
*/
|
||||
|
||||
#include "ptsToBam.h"
|
||||
|
||||
#include "config_putil.h"
|
||||
#include "geomPoints.h"
|
||||
#include "bamFile.h"
|
||||
#include "pandaNode.h"
|
||||
#include "geomNode.h"
|
||||
#include "dcast.h"
|
||||
#include "string_utils.h"
|
||||
#include "config_egg2pg.h"
|
||||
|
||||
using std::string;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
PtsToBam::
|
||||
PtsToBam() : WithOutputFile(true, false, true)
|
||||
{
|
||||
set_program_brief("convert point cloud data into a .bam file");
|
||||
set_program_description
|
||||
("This program reads a point clound in a pts file and outputs a bam files, "
|
||||
"suitable for viewing in Panda.");
|
||||
|
||||
clear_runlines();
|
||||
add_runline("[opts] input.pts output.bam");
|
||||
add_runline("[opts] -o output.bam input.pts");
|
||||
|
||||
add_option
|
||||
("o", "filename", 0,
|
||||
"Specify the filename to which the resulting .bam file will be written. "
|
||||
"If this option is omitted, the last parameter name is taken to be the "
|
||||
"name of the output file.",
|
||||
&PtsToBam::dispatch_filename, &_got_output_filename, &_output_filename);
|
||||
|
||||
add_option
|
||||
("d", "divisor", 0,
|
||||
"Decimates the point cloud by the indicated divisor. The number of points\n"
|
||||
"added is 1/divisor; numbers larger than 1.0 mean correspondingly fewer\n"
|
||||
"points.",
|
||||
&PtsToBam::dispatch_double, nullptr, &_decimate_divisor);
|
||||
|
||||
_decimate_divisor = 1.0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void PtsToBam::
|
||||
run() {
|
||||
pifstream pts;
|
||||
_pts_filename.set_text();
|
||||
if (!_pts_filename.open_read(pts)) {
|
||||
nout << "Cannot open " << _pts_filename << "\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
_gnode = new GeomNode(_pts_filename.get_basename());
|
||||
|
||||
_num_points_expected = 0;
|
||||
_num_points_found = 0;
|
||||
_num_points_added = 0;
|
||||
_decimate_factor = 1.0 / std::max(1.0, _decimate_divisor);
|
||||
_line_number = 0;
|
||||
_point_number = 0;
|
||||
_decimated_point_number = 0.0;
|
||||
_num_vdatas = 0;
|
||||
string line;
|
||||
while (std::getline(pts, line)) {
|
||||
process_line(line);
|
||||
}
|
||||
close_vertex_data();
|
||||
|
||||
nout << "\nFound " << _num_points_found << " points of " << _num_points_expected << " expected.\n";
|
||||
nout << "Generated " << _num_points_added << " points to bam file.\n";
|
||||
|
||||
// This should be guaranteed because we pass false to the constructor,
|
||||
// above.
|
||||
nassertv(has_output_filename());
|
||||
|
||||
Filename filename = get_output_filename();
|
||||
filename.make_dir();
|
||||
nout << "Writing " << filename << "\n";
|
||||
BamFile bam_file;
|
||||
if (!bam_file.open_write(filename)) {
|
||||
nout << "Error in writing.\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!bam_file.write_object(_gnode.p())) {
|
||||
nout << "Error in writing.\n";
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
bool PtsToBam::
|
||||
handle_args(ProgramBase::Args &args) {
|
||||
if (args.empty()) {
|
||||
nout << "You must specify the pts file to read on the command line.\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (args.size() > 1) {
|
||||
nout << "Specify only one pts on the command line.\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
_pts_filename = Filename::from_os_specific(args[0]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a single line from the pts file.
|
||||
*/
|
||||
void PtsToBam::
|
||||
process_line(const string &line) {
|
||||
_line_number++;
|
||||
|
||||
if (_line_number % 1000000 == 0) {
|
||||
std::cerr << "." << std::flush;
|
||||
}
|
||||
|
||||
if (line.empty() || !isdigit(line[0])) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_line_number == 1) {
|
||||
// The first line might be just the number of points.
|
||||
vector_string words;
|
||||
tokenize(trim(line), words, " \t", true);
|
||||
if (words.size() == 1) {
|
||||
string tail;
|
||||
_num_points_expected = string_to_int(words[0], tail);
|
||||
nout << "Expecting " << _num_points_expected << " points, will generate "
|
||||
<< (int)(_num_points_expected * _decimate_factor) << "\n";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Here we might have a point.
|
||||
_num_points_found++;
|
||||
_decimated_point_number += _decimate_factor;
|
||||
int point_number = int(_decimated_point_number);
|
||||
if (point_number > _point_number) {
|
||||
_point_number = point_number;
|
||||
|
||||
vector_string words;
|
||||
tokenize(trim(line), words, " \t", true);
|
||||
if (words.size() >= 3) {
|
||||
add_point(words);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a point from the pts file.
|
||||
*/
|
||||
void PtsToBam::
|
||||
add_point(const vector_string &words) {
|
||||
if (_data == nullptr || _data->get_num_rows() >= egg_max_vertices) {
|
||||
open_vertex_data();
|
||||
}
|
||||
|
||||
string tail;
|
||||
double x, y, z;
|
||||
x = string_to_double(words[0], tail);
|
||||
y = string_to_double(words[1], tail);
|
||||
z = string_to_double(words[2], tail);
|
||||
_vertex.add_data3d(x, y, z);
|
||||
_num_points_added++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GeomVertexData.
|
||||
*/
|
||||
void PtsToBam::
|
||||
open_vertex_data() {
|
||||
if (_data != nullptr) {
|
||||
close_vertex_data();
|
||||
}
|
||||
CPT(GeomVertexFormat) format = GeomVertexFormat::get_v3();
|
||||
_data = new GeomVertexData("pts", format, GeomEnums::UH_static);
|
||||
_vertex = GeomVertexWriter(_data, "vertex");
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes a previous GeomVertexData and adds it to the scene graph.
|
||||
*/
|
||||
void PtsToBam::
|
||||
close_vertex_data() {
|
||||
if (_data == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
_num_vdatas++;
|
||||
nout << "\nGenerating " << _num_points_added << " points in " << _num_vdatas << " GeomVertexDatas\n";
|
||||
|
||||
PT(Geom) geom = new Geom(_data);
|
||||
|
||||
int num_vertices = _data->get_num_rows();
|
||||
int vertices_so_far = 0;
|
||||
while (num_vertices > 0) {
|
||||
int this_num_vertices = std::min(num_vertices, (int)egg_max_indices);
|
||||
PT(GeomPrimitive) points = new GeomPoints(GeomEnums::UH_static);
|
||||
points->add_consecutive_vertices(vertices_so_far, this_num_vertices);
|
||||
geom->add_primitive(points);
|
||||
vertices_so_far += this_num_vertices;
|
||||
num_vertices -= this_num_vertices;
|
||||
}
|
||||
|
||||
_gnode->add_geom(geom);
|
||||
|
||||
_data = nullptr;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
PtsToBam prog;
|
||||
prog.parse_command_line(argc, argv);
|
||||
prog.run();
|
||||
return 0;
|
||||
}
|
||||
@ -1,64 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file ptsToBam.h
|
||||
* @author drose
|
||||
* @date 2000-06-28
|
||||
*/
|
||||
|
||||
#ifndef PTSTOBAM_H
|
||||
#define PTSTOBAM_H
|
||||
|
||||
#include "pandatoolbase.h"
|
||||
|
||||
#include "programBase.h"
|
||||
#include "withOutputFile.h"
|
||||
#include "filename.h"
|
||||
#include "vector_string.h"
|
||||
#include "geomVertexData.h"
|
||||
#include "geomVertexWriter.h"
|
||||
#include "geomNode.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class PtsToBam : public ProgramBase, public WithOutputFile {
|
||||
public:
|
||||
PtsToBam();
|
||||
|
||||
void run();
|
||||
|
||||
protected:
|
||||
virtual bool handle_args(Args &args);
|
||||
|
||||
private:
|
||||
void process_line(const std::string &line);
|
||||
void add_point(const vector_string &words);
|
||||
|
||||
void open_vertex_data();
|
||||
void close_vertex_data();
|
||||
|
||||
private:
|
||||
Filename _pts_filename;
|
||||
double _decimate_divisor;
|
||||
double _decimate_factor;
|
||||
|
||||
int _line_number;
|
||||
int _point_number;
|
||||
int _num_points_expected;
|
||||
int _num_points_found;
|
||||
int _num_points_added;
|
||||
int _num_vdatas;
|
||||
|
||||
double _decimated_point_number;
|
||||
PT(GeomNode) _gnode;
|
||||
PT(GeomVertexData) _data;
|
||||
GeomVertexWriter _vertex;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,23 +0,0 @@
|
||||
if(NOT HAVE_EGG)
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(P3CONVERTER_HEADERS
|
||||
somethingToEggConverter.h somethingToEggConverter.I
|
||||
eggToSomethingConverter.h eggToSomethingConverter.I
|
||||
)
|
||||
|
||||
set(P3CONVERTER_SOURCES
|
||||
somethingToEggConverter.cxx eggToSomethingConverter.cxx
|
||||
)
|
||||
|
||||
add_library(p3converter STATIC ${P3CONVERTER_HEADERS} ${P3CONVERTER_SOURCES})
|
||||
target_link_libraries(p3converter p3pandatoolbase pandaegg)
|
||||
|
||||
install(TARGETS p3converter
|
||||
EXPORT ToolsDevel COMPONENT ToolsDevel
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/panda3d
|
||||
ARCHIVE COMPONENT ToolsDevel)
|
||||
install(FILES ${P3CONVERTER_HEADERS} COMPONENT ToolsDevel DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/panda3d)
|
||||
@ -1,64 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file eggToSomethingConverter.I
|
||||
* @author drose
|
||||
* @date 2012-09-26
|
||||
*/
|
||||
|
||||
/**
|
||||
* Resets the error flag to the no-error state. had_error() will return false
|
||||
* until a new error is generated.
|
||||
*/
|
||||
INLINE void EggToSomethingConverter::
|
||||
clear_error() {
|
||||
_error = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if an error was detected during the conversion process, false
|
||||
* otherwise.
|
||||
*/
|
||||
INLINE bool EggToSomethingConverter::
|
||||
had_error() const {
|
||||
return _error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the EggData to NULL and makes the converter invalid.
|
||||
*/
|
||||
INLINE void EggToSomethingConverter::
|
||||
clear_egg_data() {
|
||||
set_egg_data(nullptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the EggData structure.
|
||||
*/
|
||||
INLINE EggData *EggToSomethingConverter::
|
||||
get_egg_data() {
|
||||
return _egg_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the units that the EggData has already been scaled to. This is
|
||||
* informational only; if the target file format supports it, this information
|
||||
* will be written to the header.
|
||||
*/
|
||||
void EggToSomethingConverter::
|
||||
set_output_units(DistanceUnit output_units) {
|
||||
_output_units = output_units;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value supplied to set_output_units().
|
||||
*/
|
||||
DistanceUnit EggToSomethingConverter::
|
||||
get_output_units() const {
|
||||
return _output_units;
|
||||
}
|
||||
@ -1,69 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file eggToSomethingConverter.cxx
|
||||
* @author drose
|
||||
* @date 2001-04-26
|
||||
*/
|
||||
|
||||
#include "eggToSomethingConverter.h"
|
||||
|
||||
#include "eggData.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
EggToSomethingConverter::
|
||||
EggToSomethingConverter() {
|
||||
_egg_data = nullptr;
|
||||
_error = false;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
EggToSomethingConverter::
|
||||
EggToSomethingConverter(const EggToSomethingConverter ©) {
|
||||
_egg_data = nullptr;
|
||||
_error = false;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
EggToSomethingConverter::
|
||||
~EggToSomethingConverter() {
|
||||
clear_egg_data();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the egg data that will be filled in when convert_file() is called.
|
||||
* This must be called before convert_file().
|
||||
*/
|
||||
void EggToSomethingConverter::
|
||||
set_egg_data(EggData *egg_data) {
|
||||
_egg_data = egg_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a space-separated list of extension, in addition to the one
|
||||
* returned by get_extension(), that are recognized by this converter.
|
||||
*/
|
||||
std::string EggToSomethingConverter::
|
||||
get_additional_extensions() const {
|
||||
return std::string();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this file type can transparently save compressed files
|
||||
* (with a .pz extension), false otherwise.
|
||||
*/
|
||||
bool EggToSomethingConverter::
|
||||
supports_compressed() const {
|
||||
return false;
|
||||
}
|
||||
@ -1,71 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file eggToSomethingConverter.h
|
||||
* @author drose
|
||||
* @date 2012-09-26
|
||||
*/
|
||||
|
||||
#ifndef EGGTOSOMETHINGCONVERTER_H
|
||||
#define EGGTOSOMETHINGCONVERTER_H
|
||||
|
||||
#include "pandatoolbase.h"
|
||||
|
||||
#include "filename.h"
|
||||
#include "pointerTo.h"
|
||||
#include "distanceUnit.h"
|
||||
#include "coordinateSystem.h"
|
||||
|
||||
class EggData;
|
||||
class EggGroupNode;
|
||||
|
||||
/**
|
||||
* This is a base class for a family of converter classes that manage a
|
||||
* conversion from egg format to some other file type.
|
||||
*
|
||||
* Classes of this type can be used to implement egg2xxx converter programs,
|
||||
* as well as LoaderFileTypeXXX run-time savers.
|
||||
*/
|
||||
class EggToSomethingConverter {
|
||||
public:
|
||||
EggToSomethingConverter();
|
||||
EggToSomethingConverter(const EggToSomethingConverter ©);
|
||||
virtual ~EggToSomethingConverter();
|
||||
|
||||
virtual EggToSomethingConverter *make_copy()=0;
|
||||
|
||||
INLINE void clear_error();
|
||||
INLINE bool had_error() const;
|
||||
|
||||
void set_egg_data(EggData *egg_data);
|
||||
INLINE void clear_egg_data();
|
||||
INLINE EggData *get_egg_data();
|
||||
|
||||
INLINE void set_output_units(DistanceUnit output_units);
|
||||
INLINE DistanceUnit get_output_units() const;
|
||||
INLINE void set_output_coordinate_system(CoordinateSystem output_coordinate_system) const;
|
||||
INLINE CoordinateSystem get_output_coordinate_system() const;
|
||||
|
||||
virtual std::string get_name() const=0;
|
||||
virtual std::string get_extension() const=0;
|
||||
virtual std::string get_additional_extensions() const;
|
||||
virtual bool supports_compressed() const;
|
||||
|
||||
virtual bool write_file(const Filename &filename)=0;
|
||||
|
||||
protected:
|
||||
PT(EggData) _egg_data;
|
||||
DistanceUnit _output_units;
|
||||
CoordinateSystem _output_coordinate_system;
|
||||
|
||||
bool _error;
|
||||
};
|
||||
|
||||
#include "eggToSomethingConverter.I"
|
||||
|
||||
#endif
|
||||
@ -1,391 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file somethingToEggConverter.I
|
||||
* @author drose
|
||||
* @date 2001-04-26
|
||||
*/
|
||||
|
||||
/**
|
||||
* Resets the error flag to the no-error state. had_error() will return false
|
||||
* until a new error is generated.
|
||||
*/
|
||||
INLINE void SomethingToEggConverter::
|
||||
clear_error() {
|
||||
_error = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if an error was detected during the conversion process (unless
|
||||
* _allow_errors is true), false otherwise.
|
||||
*/
|
||||
INLINE bool SomethingToEggConverter::
|
||||
had_error() const {
|
||||
return !_allow_errors && (_error || _path_replace->had_error());
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the PathReplace object (which specifies how to mangle paths from
|
||||
* the source to the destination egg file) with a new one.
|
||||
*/
|
||||
INLINE void SomethingToEggConverter::
|
||||
set_path_replace(PathReplace *path_replace) {
|
||||
_path_replace = path_replace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a pointer to the PathReplace object associated with this converter.
|
||||
* If the converter is non-const, this returns a non-const pointer, which can
|
||||
* be adjusted.
|
||||
*/
|
||||
INLINE PathReplace *SomethingToEggConverter::
|
||||
get_path_replace() {
|
||||
return _path_replace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a pointer to the PathReplace object associated with this converter.
|
||||
* If the converter is non-const, this returns a non-const pointer, which can
|
||||
* be adjusted.
|
||||
*/
|
||||
INLINE const PathReplace *SomethingToEggConverter::
|
||||
get_path_replace() const {
|
||||
return _path_replace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies how source animation will be converted into egg structures. The
|
||||
* default is AC_none, which means animation tables will be ignored. This is
|
||||
* only meaningful for converters that understand animation.
|
||||
*/
|
||||
INLINE void SomethingToEggConverter::
|
||||
set_animation_convert(AnimationConvert animation_convert) {
|
||||
_animation_convert = animation_convert;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns how source animation will be converted into egg structures.
|
||||
*/
|
||||
INLINE AnimationConvert SomethingToEggConverter::
|
||||
get_animation_convert() const {
|
||||
return _animation_convert;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the name of the character generated. This name should match
|
||||
* between all the model and channel egg files for a particular character and
|
||||
* its associated animations.
|
||||
*/
|
||||
INLINE void SomethingToEggConverter::
|
||||
set_character_name(const std::string &character_name) {
|
||||
_character_name = character_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the character generated. See set_character_name().
|
||||
*/
|
||||
INLINE const std::string &SomethingToEggConverter::
|
||||
get_character_name() const {
|
||||
return _character_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the starting frame of the animation to convert, in the units
|
||||
* specified by set_input_frame_rate(). If this is unspecified, the starting
|
||||
* frame is taken from the source, for instance from the first frame of the
|
||||
* animation slider.
|
||||
*/
|
||||
INLINE void SomethingToEggConverter::
|
||||
set_start_frame(double start_frame) {
|
||||
_start_frame = start_frame;
|
||||
_control_flags |= CF_start_frame;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the starting frame has been explicitly specified via
|
||||
* set_start_frame(), or false if the starting frame should be implicit based
|
||||
* on the source.
|
||||
*/
|
||||
INLINE bool SomethingToEggConverter::
|
||||
has_start_frame() const {
|
||||
return (_control_flags & CF_start_frame) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value set by a previous call to set_start_frame(). It is an
|
||||
* error to call this if has_start_frame() returns false.
|
||||
*/
|
||||
INLINE double SomethingToEggConverter::
|
||||
get_start_frame() const {
|
||||
nassertr(has_start_frame(), 0.0);
|
||||
return _start_frame;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the value previously set by set_start_frame().
|
||||
*/
|
||||
INLINE void SomethingToEggConverter::
|
||||
clear_start_frame() {
|
||||
_start_frame = 0.0;
|
||||
_control_flags &= ~CF_start_frame;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the ending frame of the animation to convert, in the units
|
||||
* specified by set_input_frame_rate(). If this is unspecified, the ending
|
||||
* frame is taken from the source, for instance from the last frame of the
|
||||
* animation slider.
|
||||
*/
|
||||
INLINE void SomethingToEggConverter::
|
||||
set_end_frame(double end_frame) {
|
||||
_end_frame = end_frame;
|
||||
_control_flags |= CF_end_frame;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the ending frame has been explicitly specified via
|
||||
* set_end_frame(), or false if the ending frame should be implicit based on
|
||||
* the source.
|
||||
*/
|
||||
INLINE bool SomethingToEggConverter::
|
||||
has_end_frame() const {
|
||||
return (_control_flags & CF_end_frame) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value set by a previous call to set_end_frame(). It is an
|
||||
* error to call this if has_end_frame() returns false.
|
||||
*/
|
||||
INLINE double SomethingToEggConverter::
|
||||
get_end_frame() const {
|
||||
nassertr(has_end_frame(), 0.0);
|
||||
return _end_frame;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the value previously set by set_end_frame().
|
||||
*/
|
||||
INLINE void SomethingToEggConverter::
|
||||
clear_end_frame() {
|
||||
_end_frame = 0.0;
|
||||
_control_flags &= ~CF_end_frame;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the increment between frames to extract. This is the amount to
|
||||
* increment the time slider (in units of internal_frame_rate) between
|
||||
* extracting each frame. If this is not specified, the default is taken from
|
||||
* the animation package, or 1.0 if the animation package does not specified a
|
||||
* frame increment.
|
||||
*/
|
||||
INLINE void SomethingToEggConverter::
|
||||
set_frame_inc(double frame_inc) {
|
||||
_frame_inc = frame_inc;
|
||||
_control_flags |= CF_frame_inc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the frame increment has been explicitly specified via
|
||||
* set_frame_inc(), or false if the ending frame should be implicit based on
|
||||
* the source.
|
||||
*/
|
||||
INLINE bool SomethingToEggConverter::
|
||||
has_frame_inc() const {
|
||||
return (_control_flags & CF_frame_inc) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value set by a previous call to set_frame_inc(). It is an
|
||||
* error to call this if has_frame_inc() returns false.
|
||||
*/
|
||||
INLINE double SomethingToEggConverter::
|
||||
get_frame_inc() const {
|
||||
nassertr(has_frame_inc(), 0.0);
|
||||
return _frame_inc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the value previously set by set_frame_inc().
|
||||
*/
|
||||
INLINE void SomethingToEggConverter::
|
||||
clear_frame_inc() {
|
||||
_frame_inc = 0.0;
|
||||
_control_flags &= ~CF_frame_inc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the frame of animation to represent the neutral pose of the
|
||||
* model.
|
||||
*/
|
||||
INLINE void SomethingToEggConverter::
|
||||
set_neutral_frame(double neutral_frame) {
|
||||
_neutral_frame = neutral_frame;
|
||||
_control_flags |= CF_neutral_frame;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the neutral frame has been explicitly specified via
|
||||
* set_neutral_frame(), or false otherwise.
|
||||
*/
|
||||
INLINE bool SomethingToEggConverter::
|
||||
has_neutral_frame() const {
|
||||
return (_control_flags & CF_neutral_frame) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value set by a previous call to set_neutral_frame(). It is an
|
||||
* error to call this if has_neutral_frame() returns false.
|
||||
*/
|
||||
INLINE double SomethingToEggConverter::
|
||||
get_neutral_frame() const {
|
||||
nassertr(has_neutral_frame(), 0.0);
|
||||
return _neutral_frame;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the value previously set by set_neutral_frame().
|
||||
*/
|
||||
INLINE void SomethingToEggConverter::
|
||||
clear_neutral_frame() {
|
||||
_neutral_frame = 0.0;
|
||||
_control_flags &= ~CF_neutral_frame;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the number of frames per second that is represented by the
|
||||
* "frame" unit in the animation package. If this is omitted, it is taken
|
||||
* from whatever the file header indicates. Some animation packages do not
|
||||
* encode a frame rate, in which case the default if this is omitted is the
|
||||
* same as the output frame rate.
|
||||
*/
|
||||
INLINE void SomethingToEggConverter::
|
||||
set_input_frame_rate(double input_frame_rate) {
|
||||
_input_frame_rate = input_frame_rate;
|
||||
_control_flags |= CF_input_frame_rate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the frame rate has been explicitly specified via
|
||||
* set_input_frame_rate(), or false otherwise.
|
||||
*/
|
||||
INLINE bool SomethingToEggConverter::
|
||||
has_input_frame_rate() const {
|
||||
return (_control_flags & CF_input_frame_rate) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value set by a previous call to set_input_frame_rate(). It is
|
||||
* an error to call this if has_input_frame_rate() returns false.
|
||||
*/
|
||||
INLINE double SomethingToEggConverter::
|
||||
get_input_frame_rate() const {
|
||||
nassertr(has_input_frame_rate(), 0.0);
|
||||
return _input_frame_rate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the value previously set by set_input_frame_rate().
|
||||
*/
|
||||
INLINE void SomethingToEggConverter::
|
||||
clear_input_frame_rate() {
|
||||
_input_frame_rate = 0.0;
|
||||
_control_flags &= ~CF_input_frame_rate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the number of frames per second that the resulting animation
|
||||
* should be played at. If this is omitted, it is taken to be the same as the
|
||||
* input frame rate.
|
||||
*/
|
||||
INLINE void SomethingToEggConverter::
|
||||
set_output_frame_rate(double output_frame_rate) {
|
||||
_output_frame_rate = output_frame_rate;
|
||||
_control_flags |= CF_output_frame_rate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the frame rate has been explicitly specified via
|
||||
* set_output_frame_rate(), or false otherwise.
|
||||
*/
|
||||
INLINE bool SomethingToEggConverter::
|
||||
has_output_frame_rate() const {
|
||||
return (_control_flags & CF_output_frame_rate) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value set by a previous call to set_output_frame_rate(). It is
|
||||
* an error to call this if has_output_frame_rate() returns false.
|
||||
*/
|
||||
INLINE double SomethingToEggConverter::
|
||||
get_output_frame_rate() const {
|
||||
nassertr(has_output_frame_rate(), 0.0);
|
||||
return _output_frame_rate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the value previously set by set_output_frame_rate().
|
||||
*/
|
||||
INLINE void SomethingToEggConverter::
|
||||
clear_output_frame_rate() {
|
||||
_output_frame_rate = 0.0;
|
||||
_control_flags &= ~CF_output_frame_rate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default frame rate if nothing is specified for input_frame_rate
|
||||
* or output_frame_rate, and the animation package does not have an implicit
|
||||
* frame rate.
|
||||
*/
|
||||
INLINE double SomethingToEggConverter::
|
||||
get_default_frame_rate() {
|
||||
return 24.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the merge_externals flag. When this is true, external references
|
||||
* within the source file are read in and merged directly; otherwise, only a
|
||||
* reference to a similarly-named egg file is inserted.
|
||||
*/
|
||||
INLINE void SomethingToEggConverter::
|
||||
set_merge_externals(bool merge_externals) {
|
||||
_merge_externals = merge_externals;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current state of the merge_externals flag. See
|
||||
* set_merge_externals().
|
||||
*/
|
||||
INLINE bool SomethingToEggConverter::
|
||||
get_merge_externals() const {
|
||||
return _merge_externals;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the EggData to NULL and makes the converter invalid.
|
||||
*/
|
||||
INLINE void SomethingToEggConverter::
|
||||
clear_egg_data() {
|
||||
set_egg_data(nullptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the EggData structure.
|
||||
*/
|
||||
INLINE EggData *SomethingToEggConverter::
|
||||
get_egg_data() {
|
||||
return _egg_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the indicated model filename to a relative or absolute or whatever
|
||||
* filename, according to _path_replace.
|
||||
*/
|
||||
INLINE Filename SomethingToEggConverter::
|
||||
convert_model_path(const Filename &orig_filename) {
|
||||
return _path_replace->convert_path(orig_filename);
|
||||
}
|
||||
@ -1,165 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file somethingToEggConverter.cxx
|
||||
* @author drose
|
||||
* @date 2001-04-26
|
||||
*/
|
||||
|
||||
#include "somethingToEggConverter.h"
|
||||
|
||||
#include "eggData.h"
|
||||
#include "eggExternalReference.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
SomethingToEggConverter::
|
||||
SomethingToEggConverter() {
|
||||
_allow_errors = false;
|
||||
_path_replace = new PathReplace;
|
||||
_path_replace->_path_store = PS_absolute;
|
||||
_animation_convert = AC_none;
|
||||
_start_frame = 0.0;
|
||||
_end_frame = 0.0;
|
||||
_frame_inc = 0.0;
|
||||
_neutral_frame = 0.0;
|
||||
_input_frame_rate = 0.0;
|
||||
_output_frame_rate = 0.0;
|
||||
_control_flags = 0;
|
||||
_merge_externals = false;
|
||||
_egg_data = nullptr;
|
||||
_error = false;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
SomethingToEggConverter::
|
||||
SomethingToEggConverter(const SomethingToEggConverter ©) :
|
||||
_allow_errors(copy._allow_errors),
|
||||
_path_replace(copy._path_replace),
|
||||
_merge_externals(copy._merge_externals)
|
||||
{
|
||||
_egg_data = nullptr;
|
||||
_error = false;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
SomethingToEggConverter::
|
||||
~SomethingToEggConverter() {
|
||||
clear_egg_data();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the egg data that will be filled in when convert_file() is called.
|
||||
* This must be called before convert_file().
|
||||
*/
|
||||
void SomethingToEggConverter::
|
||||
set_egg_data(EggData *egg_data) {
|
||||
_egg_data = egg_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a space-separated list of extension, in addition to the one
|
||||
* returned by get_extension(), that are recognized by this converter.
|
||||
*/
|
||||
std::string SomethingToEggConverter::
|
||||
get_additional_extensions() const {
|
||||
return std::string();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this file type can transparently load compressed files
|
||||
* (with a .pz extension), false otherwise.
|
||||
*/
|
||||
bool SomethingToEggConverter::
|
||||
supports_compressed() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this converter can directly convert the model type to
|
||||
* internal Panda memory structures, given the indicated options, or false
|
||||
* otherwise. If this returns true, then convert_to_node() may be called to
|
||||
* perform the conversion, which may be faster than calling convert_file() if
|
||||
* the ultimate goal is a PandaNode anyway.
|
||||
*/
|
||||
bool SomethingToEggConverter::
|
||||
supports_convert_to_node(const LoaderOptions &options) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 SomethingToEggConverter::
|
||||
get_input_units() {
|
||||
return DU_invalid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the input file and directly produces a ready-to-render model file as
|
||||
* a PandaNode. Returns NULL on failure, or if it is not supported. (This
|
||||
* functionality is not supported by all converter types; see
|
||||
* supports_convert_to_node()).
|
||||
*/
|
||||
PT(PandaNode) SomethingToEggConverter::
|
||||
convert_to_node(const LoaderOptions &options, const Filename &filename) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles an external reference in the source file. If the merge_externals
|
||||
* flag is true (see set_merge_externals()), this causes the named file to be
|
||||
* read in and converted, and the converted egg geometry is parented to
|
||||
* egg_parent. Otherwise, only a reference to a similarly named egg file is
|
||||
* parented to egg_parent.
|
||||
*
|
||||
* The parameters orig_filename and searchpath are as those passed to
|
||||
* convert_model_path().
|
||||
*
|
||||
* Returns true on success, false on failure.
|
||||
*/
|
||||
bool SomethingToEggConverter::
|
||||
handle_external_reference(EggGroupNode *egg_parent,
|
||||
const Filename &ref_filename) {
|
||||
if (_merge_externals) {
|
||||
SomethingToEggConverter *ext = make_copy();
|
||||
PT(EggData) egg_data = new EggData;
|
||||
egg_data->set_coordinate_system(get_egg_data()->get_coordinate_system());
|
||||
ext->set_egg_data(egg_data);
|
||||
|
||||
if (!ext->convert_file(ref_filename)) {
|
||||
delete ext;
|
||||
nout << "Unable to read external reference: " << ref_filename << "\n";
|
||||
_error = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
egg_parent->steal_children(*egg_data);
|
||||
delete ext;
|
||||
return true;
|
||||
|
||||
} else {
|
||||
// If we're installing external references instead of reading them, we
|
||||
// should make it into an egg filename.
|
||||
Filename filename = ref_filename;
|
||||
filename.set_extension("egg");
|
||||
|
||||
EggExternalReference *egg_ref = new EggExternalReference("", filename);
|
||||
egg_parent->add_child(egg_ref);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -1,148 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file somethingToEggConverter.h
|
||||
* @author drose
|
||||
* @date 2001-04-17
|
||||
*/
|
||||
|
||||
#ifndef SOMETHINGTOEGGCONVERTER_H
|
||||
#define SOMETHINGTOEGGCONVERTER_H
|
||||
|
||||
#include "pandatoolbase.h"
|
||||
|
||||
#include "filename.h"
|
||||
#include "config_putil.h" // for get_model_path()
|
||||
#include "animationConvert.h"
|
||||
#include "pathReplace.h"
|
||||
#include "pointerTo.h"
|
||||
#include "distanceUnit.h"
|
||||
#include "pandaNode.h"
|
||||
|
||||
class EggData;
|
||||
class EggGroupNode;
|
||||
class LoaderOptions;
|
||||
|
||||
/**
|
||||
* This is a base class for a family of converter classes that manage a
|
||||
* conversion from some file type to egg format.
|
||||
*
|
||||
* Classes of this type can be used to implement xxx2egg converter programs,
|
||||
* as well as LoaderFileTypeXXX run-time loaders.
|
||||
*/
|
||||
class SomethingToEggConverter {
|
||||
public:
|
||||
SomethingToEggConverter();
|
||||
SomethingToEggConverter(const SomethingToEggConverter ©);
|
||||
virtual ~SomethingToEggConverter();
|
||||
|
||||
virtual SomethingToEggConverter *make_copy()=0;
|
||||
|
||||
INLINE void clear_error();
|
||||
INLINE bool had_error() const;
|
||||
|
||||
INLINE void set_path_replace(PathReplace *path_replace);
|
||||
INLINE PathReplace *get_path_replace();
|
||||
INLINE const PathReplace *get_path_replace() const;
|
||||
|
||||
// These methods dealing with animation and frame rate are only relevant to
|
||||
// converter types that understand animation.
|
||||
INLINE void set_animation_convert(AnimationConvert animation_convert);
|
||||
INLINE AnimationConvert get_animation_convert() const;
|
||||
|
||||
INLINE void set_character_name(const std::string &character_name);
|
||||
INLINE const std::string &get_character_name() const;
|
||||
|
||||
INLINE void set_start_frame(double start_frame);
|
||||
INLINE bool has_start_frame() const;
|
||||
INLINE double get_start_frame() const;
|
||||
INLINE void clear_start_frame();
|
||||
|
||||
INLINE void set_end_frame(double end_frame);
|
||||
INLINE bool has_end_frame() const;
|
||||
INLINE double get_end_frame() const;
|
||||
INLINE void clear_end_frame();
|
||||
|
||||
INLINE void set_frame_inc(double frame_inc);
|
||||
INLINE bool has_frame_inc() const;
|
||||
INLINE double get_frame_inc() const;
|
||||
INLINE void clear_frame_inc();
|
||||
|
||||
INLINE void set_neutral_frame(double neutral_frame);
|
||||
INLINE bool has_neutral_frame() const;
|
||||
INLINE double get_neutral_frame() const;
|
||||
INLINE void clear_neutral_frame();
|
||||
|
||||
INLINE void set_input_frame_rate(double input_frame_rate);
|
||||
INLINE bool has_input_frame_rate() const;
|
||||
INLINE double get_input_frame_rate() const;
|
||||
INLINE void clear_input_frame_rate();
|
||||
|
||||
INLINE void set_output_frame_rate(double output_frame_rate);
|
||||
INLINE bool has_output_frame_rate() const;
|
||||
INLINE double get_output_frame_rate() const;
|
||||
INLINE void clear_output_frame_rate();
|
||||
|
||||
INLINE static double get_default_frame_rate();
|
||||
|
||||
INLINE void set_merge_externals(bool merge_externals);
|
||||
INLINE bool get_merge_externals() const;
|
||||
|
||||
void set_egg_data(EggData *egg_data);
|
||||
INLINE void clear_egg_data();
|
||||
INLINE EggData *get_egg_data();
|
||||
|
||||
virtual std::string get_name() const=0;
|
||||
virtual std::string get_extension() const=0;
|
||||
virtual std::string get_additional_extensions() const;
|
||||
virtual bool supports_compressed() const;
|
||||
virtual bool supports_convert_to_node(const LoaderOptions &options) const;
|
||||
|
||||
virtual bool convert_file(const Filename &filename)=0;
|
||||
virtual PT(PandaNode) convert_to_node(const LoaderOptions &options, const Filename &filename);
|
||||
virtual DistanceUnit get_input_units();
|
||||
|
||||
bool handle_external_reference(EggGroupNode *egg_parent,
|
||||
const Filename &ref_filename);
|
||||
|
||||
INLINE Filename convert_model_path(const Filename &orig_filename);
|
||||
|
||||
// Set this true to treat errors as warnings and generate output anyway.
|
||||
bool _allow_errors;
|
||||
|
||||
protected:
|
||||
PT(PathReplace) _path_replace;
|
||||
|
||||
AnimationConvert _animation_convert;
|
||||
std::string _character_name;
|
||||
double _start_frame;
|
||||
double _end_frame;
|
||||
double _frame_inc;
|
||||
double _neutral_frame;
|
||||
double _input_frame_rate; // frames per second
|
||||
double _output_frame_rate; // frames per second
|
||||
enum ControlFlags {
|
||||
CF_start_frame = 0x0001,
|
||||
CF_end_frame = 0x0002,
|
||||
CF_frame_inc = 0x0004,
|
||||
CF_neutral_frame = 0x0008,
|
||||
CF_input_frame_rate = 0x0010,
|
||||
CF_output_frame_rate = 0x0020,
|
||||
};
|
||||
int _control_flags;
|
||||
|
||||
bool _merge_externals;
|
||||
|
||||
PT(EggData) _egg_data;
|
||||
|
||||
bool _error;
|
||||
};
|
||||
|
||||
#include "somethingToEggConverter.I"
|
||||
|
||||
#endif
|
||||
@ -1,26 +0,0 @@
|
||||
if(NOT HAVE_EGG OR NOT HAVE_FCOLLADA)
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(P3DAEEGG_HEADERS
|
||||
config_daeegg.h
|
||||
daeCharacter.h
|
||||
daeMaterials.h
|
||||
daeToEggConverter.h
|
||||
fcollada_utils.h
|
||||
pre_fcollada_include.h
|
||||
)
|
||||
|
||||
set(P3DAEEGG_SOURCES
|
||||
config_daeegg.cxx
|
||||
daeCharacter.cxx
|
||||
daeMaterials.cxx
|
||||
daeToEggConverter.cxx
|
||||
)
|
||||
|
||||
add_library(p3daeegg STATIC ${P3DAEEGG_HEADERS} ${P3DAEEGG_SOURCES})
|
||||
target_link_libraries(p3daeegg p3eggbase
|
||||
PKG::FCOLLADA)
|
||||
|
||||
# This is only needed for binaries in the pandatool package. It is not useful
|
||||
# for user applications, so it is not installed.
|
||||
@ -1,43 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file config_daeegg.cxx
|
||||
* @author rdb
|
||||
* @date 2008-10-30
|
||||
*/
|
||||
|
||||
#include "config_daeegg.h"
|
||||
#include "daeCharacter.h"
|
||||
#include "daeMaterials.h"
|
||||
|
||||
#include "dconfig.h"
|
||||
|
||||
Configure(config_daeegg);
|
||||
NotifyCategoryDef(daeegg, "");
|
||||
|
||||
ConfigureFn(config_daeegg) {
|
||||
init_libdaeegg();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the library. This must be called at least once before any of
|
||||
* the functions or classes in this library can be used. Normally it will be
|
||||
* called by the static initializers and need not be called explicitly, but
|
||||
* special cases exist.
|
||||
*/
|
||||
void
|
||||
init_libdaeegg() {
|
||||
static bool initialized = false;
|
||||
if (initialized) {
|
||||
return;
|
||||
}
|
||||
initialized = true;
|
||||
|
||||
DaeCharacter::init_type();
|
||||
DaeMaterials::init_type();
|
||||
}
|
||||
@ -1,24 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file config_daeegg.h
|
||||
* @author rdb
|
||||
* @date 2008-10-30
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_DAEEGG_H
|
||||
#define CONFIG_DAEEGG_H
|
||||
|
||||
#include "pandatoolbase.h"
|
||||
#include "notifyCategoryProxy.h"
|
||||
|
||||
NotifyCategoryDeclNoExport(daeegg);
|
||||
|
||||
extern void init_libdaeegg();
|
||||
|
||||
#endif
|
||||
@ -1,312 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file daeCharacter.cxx
|
||||
* @author rdb
|
||||
* @date 2008-11-24
|
||||
*/
|
||||
|
||||
#include "daeCharacter.h"
|
||||
#include "config_daeegg.h"
|
||||
#include "fcollada_utils.h"
|
||||
#include "pt_EggVertex.h"
|
||||
#include "eggXfmSAnim.h"
|
||||
#include "daeToEggConverter.h"
|
||||
#include "daeMaterials.h"
|
||||
|
||||
#include "eggExternalReference.h"
|
||||
|
||||
#include <FCDocument/FCDocument.h>
|
||||
#include <FCDocument/FCDController.h>
|
||||
#include <FCDocument/FCDGeometry.h>
|
||||
#include <FCDocument/FCDSceneNodeTools.h>
|
||||
|
||||
#include <FCDocument/FCDSceneNode.h>
|
||||
#include <FCDocument/FCDTransform.h>
|
||||
#include <FCDocument/FCDAnimated.h>
|
||||
#include <FCDocument/FCDAnimationCurve.h>
|
||||
#include <FCDocument/FCDAnimationKey.h>
|
||||
|
||||
TypeHandle DaeCharacter::_type_handle;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
DaeCharacter::
|
||||
DaeCharacter(EggGroup *node_group, const FCDControllerInstance *instance) :
|
||||
_node_group(node_group),
|
||||
_skin_mesh(nullptr),
|
||||
_instance(instance),
|
||||
_bind_shape_mat(LMatrix4d::ident_mat()),
|
||||
_name(node_group->get_name()),
|
||||
_skin_controller(nullptr) {
|
||||
|
||||
// If it's a skin controller, add the controller joints.
|
||||
const FCDController *controller = (const FCDController *)instance->GetEntity();
|
||||
if (controller == nullptr) {
|
||||
return;
|
||||
}
|
||||
_skin_mesh = controller->GetBaseGeometry()->GetMesh();
|
||||
|
||||
if (controller->IsSkin()) {
|
||||
_skin_controller = controller->GetSkinController();
|
||||
_bind_shape_mat = DAEToEggConverter::convert_matrix(_skin_controller->GetBindShapeTransform());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds the joints to the character. This means changing them to the bind
|
||||
* pose. It is necessary to call this before process_skin_geometry.
|
||||
*
|
||||
* Returns the root group.
|
||||
*/
|
||||
void DaeCharacter::
|
||||
bind_joints(JointMap &joint_map) {
|
||||
_joints.clear();
|
||||
|
||||
size_t num_joints = _skin_controller->GetJointCount();
|
||||
_joints.reserve(num_joints);
|
||||
|
||||
// Record the bind pose for each joint.
|
||||
for (size_t j = 0; j < num_joints; ++j) {
|
||||
const FCDSkinControllerJoint *skin_joint = _skin_controller->GetJoint(j);
|
||||
std::string sid = FROM_FSTRING(skin_joint->GetId());
|
||||
LMatrix4d bind_pose;
|
||||
bind_pose.invert_from(DAEToEggConverter::convert_matrix(
|
||||
skin_joint->GetBindPoseInverse()));
|
||||
|
||||
// Check that we already encountered this joint during traversal.
|
||||
JointMap::iterator ji = joint_map.find(sid);
|
||||
if (ji != joint_map.end()) {
|
||||
Joint &joint = ji->second;
|
||||
|
||||
if (joint._character != nullptr) {
|
||||
// In some cases, though, multiple controllers share the same joints.
|
||||
// We can't support this without duplicating the joint structure, so
|
||||
// we check if the bind poses are the same.
|
||||
if (!joint._bind_pose.almost_equal(bind_pose, 0.0001)) {
|
||||
// Ugh. What else could we do?
|
||||
daeegg_cat.error()
|
||||
<< "Multiple controllers share joint with sid " << sid
|
||||
<< ", with different bind poses.\n";
|
||||
}
|
||||
} else {
|
||||
// Mark the joint as being controlled by this character.
|
||||
joint._bind_pose = bind_pose;
|
||||
joint._character = this;
|
||||
}
|
||||
|
||||
_joints.push_back(joint);
|
||||
} else {
|
||||
daeegg_cat.warning()
|
||||
<< "Unknown joint sid being referenced: '" << sid << "'\n";
|
||||
|
||||
// We still have to add a dummy joint or the index will be off.
|
||||
_joints.push_back(Joint(nullptr, nullptr));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Traverses through the character hierarchy in order to bind the mesh to the
|
||||
* character. This involves reorienting the joints to match the bind pose.
|
||||
*
|
||||
* It is important that this is called only once.
|
||||
*/
|
||||
void DaeCharacter::
|
||||
adjust_joints(FCDSceneNode *node, const JointMap &joint_map,
|
||||
const LMatrix4d &transform) {
|
||||
|
||||
LMatrix4d this_transform = transform;
|
||||
|
||||
if (node->IsJoint()) {
|
||||
std::string sid = FROM_FSTRING(node->GetSubId());
|
||||
|
||||
JointMap::const_iterator ji = joint_map.find(sid);
|
||||
if (ji != joint_map.end()) {
|
||||
const Joint &joint = ji->second;
|
||||
|
||||
// Panda needs the joints to be in bind pose. Not fun! We copy the
|
||||
// joint transform to the default pose, though, so that Panda will
|
||||
// restore the joint transformation after binding.
|
||||
|
||||
if (joint._character == this) {
|
||||
LMatrix4d bind_pose = joint._bind_pose * _bind_shape_mat *
|
||||
invert(transform);
|
||||
// LMatrix4d bind_pose = joint._bind_pose * _bind_shape_mat *
|
||||
// joint._group->get_parent()->get_node_frame_inv();
|
||||
|
||||
this_transform = bind_pose * this_transform;
|
||||
joint._group->set_default_pose(*joint._group);
|
||||
joint._group->set_transform3d(bind_pose);
|
||||
|
||||
/*
|
||||
PT(EggGroup) sphere = new EggGroup;
|
||||
sphere->add_uniform_scale(0.1);
|
||||
sphere->set_group_type(EggGroup::GT_instance);
|
||||
sphere->add_child(new EggExternalReference("", "jack.egg"));
|
||||
joint._group->add_child(sphere);
|
||||
*/
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// this_transform = DAEToEggConverter::convert_matrix(node->ToMatrix());
|
||||
}
|
||||
|
||||
// Loop through the children joints
|
||||
for (size_t ch = 0; ch < node->GetChildrenCount(); ++ch) {
|
||||
// if (node->GetChild(ch)->IsJoint()) {
|
||||
adjust_joints(node->GetChild(ch), joint_map, this_transform);
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the influences for the given vertex.
|
||||
*/
|
||||
void DaeCharacter::
|
||||
influence_vertex(int index, EggVertex *vertex) {
|
||||
const FCDSkinControllerVertex *influence = _skin_controller->GetVertexInfluence(index);
|
||||
|
||||
for (size_t pa = 0; pa < influence->GetPairCount(); ++pa) {
|
||||
const FCDJointWeightPair* jwpair = influence->GetPair(pa);
|
||||
|
||||
if (jwpair->jointIndex >= 0 && jwpair->jointIndex < (int)_joints.size()) {
|
||||
EggGroup *joint = _joints[jwpair->jointIndex]._group.p();
|
||||
if (joint != nullptr) {
|
||||
joint->ref_vertex(vertex, jwpair->weight);
|
||||
}
|
||||
} else {
|
||||
daeegg_cat.error()
|
||||
<< "Invalid joint index: " << jwpair->jointIndex << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects all animation keys of animations applied to this character.
|
||||
*/
|
||||
void DaeCharacter::
|
||||
collect_keys(pset<float> &keys) {
|
||||
#if FCOLLADA_VERSION < 0x00030005
|
||||
FCDSceneNodeList roots = _instance->FindSkeletonNodes();
|
||||
#else
|
||||
FCDSceneNodeList roots;
|
||||
_instance->FindSkeletonNodes(roots);
|
||||
#endif
|
||||
|
||||
for (FCDSceneNodeList::iterator it = roots.begin(); it != roots.end(); ++it) {
|
||||
r_collect_keys(*it, keys);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects all animation keys found for the given node tree.
|
||||
*/
|
||||
void DaeCharacter::
|
||||
r_collect_keys(FCDSceneNode* node, pset<float> &keys) {
|
||||
FCDAnimatedList animateds;
|
||||
|
||||
// Collect all the animation curves
|
||||
for (size_t t = 0; t < node->GetTransformCount(); ++t) {
|
||||
FCDTransform *transform = node->GetTransform(t);
|
||||
FCDAnimated *animated = transform->GetAnimated();
|
||||
|
||||
if (animated != nullptr) {
|
||||
const FCDAnimationCurveListList &all_curves = animated->GetCurves();
|
||||
|
||||
for (size_t ci = 0; ci < all_curves.size(); ++ci) {
|
||||
const FCDAnimationCurveTrackList &curves = all_curves[ci];
|
||||
if (curves.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t num_keys = curves.front()->GetKeyCount();
|
||||
const FCDAnimationKey **curve_keys = curves.front()->GetKeys();
|
||||
|
||||
for (size_t c = 0; c < num_keys; ++c) {
|
||||
keys.insert(curve_keys[c]->input);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a joint node and its transforms.
|
||||
*/
|
||||
void DaeCharacter::
|
||||
build_table(EggTable *parent, FCDSceneNode* node, const pset<float> &keys) {
|
||||
nassertv(node != nullptr);
|
||||
|
||||
if (!node->IsJoint()) {
|
||||
for (size_t ch = 0; ch < node->GetChildrenCount(); ++ch) {
|
||||
build_table(parent, node->GetChild(ch), keys);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
std::string node_id = FROM_FSTRING(node->GetDaeId());
|
||||
PT(EggTable) table = new EggTable(node_id);
|
||||
table->set_table_type(EggTable::TT_table);
|
||||
parent->add_child(table);
|
||||
|
||||
PT(EggXfmSAnim) xform = new EggXfmSAnim("xform");
|
||||
table->add_child(xform);
|
||||
|
||||
// Generate the sampled animation and loop through the matrices
|
||||
FCDAnimatedList animateds;
|
||||
|
||||
// Collect all the animation curves
|
||||
for (size_t t = 0; t < node->GetTransformCount(); ++t) {
|
||||
FCDTransform *transform = node->GetTransform(t);
|
||||
FCDAnimated *animated = transform->GetAnimated();
|
||||
if (animated != nullptr) {
|
||||
if (animated->HasCurve()) {
|
||||
animateds.push_back(animated);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sample the scene node transform
|
||||
float last_key;
|
||||
float timing_total = 0;
|
||||
pset<float>::const_iterator ki;
|
||||
for (ki = keys.begin(); ki != keys.end(); ++ki) {
|
||||
for (FCDAnimatedList::iterator it = animateds.begin(); it != animateds.end(); ++it) {
|
||||
// Sample each animated, which changes the transform values directly
|
||||
(*it)->Evaluate(*ki);
|
||||
}
|
||||
|
||||
if (ki != keys.begin()) {
|
||||
timing_total += (*ki - last_key);
|
||||
}
|
||||
last_key = *ki;
|
||||
|
||||
// Retrieve the new transform matrix for the COLLADA scene node
|
||||
FMMatrix44 fmat = node->ToMatrix();
|
||||
|
||||
// Work around issue in buggy exporters (like ColladaMax)
|
||||
if (IS_NEARLY_ZERO(fmat[3][3])) {
|
||||
fmat[3][3] = 1;
|
||||
}
|
||||
|
||||
xform->add_data(DAEToEggConverter::convert_matrix(fmat));
|
||||
}
|
||||
|
||||
// Quantize the FPS, otherwise Panda complains about FPS mismatches.
|
||||
float fps = cfloor(((keys.size() - 1) / timing_total) * 100 + 0.5f) * 0.01f;
|
||||
xform->set_fps(fps);
|
||||
|
||||
// Loop through the children joints
|
||||
for (size_t ch = 0; ch < node->GetChildrenCount(); ++ch) {
|
||||
// if (node->GetChild(ch)->IsJoint()) {
|
||||
build_table(table, node->GetChild(ch), keys);
|
||||
// }
|
||||
}
|
||||
}
|
||||
@ -1,95 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file daeCharacter.h
|
||||
* @author rdb
|
||||
* @date 2008-11-24
|
||||
*/
|
||||
|
||||
#ifndef DAECHARACTER_H
|
||||
#define DAECHARACTER_H
|
||||
|
||||
#include "pandatoolbase.h"
|
||||
#include "typedReferenceCount.h"
|
||||
#include "typeHandle.h"
|
||||
#include "eggTable.h"
|
||||
#include "epvector.h"
|
||||
|
||||
#include "pre_fcollada_include.h"
|
||||
#include <FCollada.h>
|
||||
#include <FCDocument/FCDSceneNode.h>
|
||||
#include <FCDocument/FCDControllerInstance.h>
|
||||
#include <FCDocument/FCDSkinController.h>
|
||||
#include <FCDocument/FCDGeometryMesh.h>
|
||||
|
||||
class DAEToEggConverter;
|
||||
|
||||
/**
|
||||
* Class representing an animated character.
|
||||
*/
|
||||
class DaeCharacter : public TypedReferenceCount {
|
||||
public:
|
||||
DaeCharacter(EggGroup *node_group, const FCDControllerInstance* controller_instance);
|
||||
|
||||
struct Joint {
|
||||
INLINE Joint(EggGroup *group, const FCDSceneNode *scene_node) :
|
||||
_bind_pose(LMatrix4d::ident_mat()),
|
||||
_group(group),
|
||||
_scene_node(scene_node),
|
||||
_character(nullptr) {}
|
||||
|
||||
LMatrix4d _bind_pose;
|
||||
PT(EggGroup) _group;
|
||||
const FCDSceneNode *_scene_node;
|
||||
DaeCharacter *_character;
|
||||
};
|
||||
typedef epvector<Joint> Joints;
|
||||
typedef pmap<std::string, Joint> JointMap;
|
||||
|
||||
void bind_joints(JointMap &joint_map);
|
||||
void adjust_joints(FCDSceneNode *node, const JointMap &joint_map,
|
||||
const LMatrix4d &transform = LMatrix4d::ident_mat());
|
||||
|
||||
void influence_vertex(int index, EggVertex *vertex);
|
||||
|
||||
void collect_keys(pset<float> &keys);
|
||||
void r_collect_keys(FCDSceneNode *node, pset<float> &keys);
|
||||
|
||||
void build_table(EggTable *parent, FCDSceneNode* node, const pset<float> &keys);
|
||||
|
||||
public:
|
||||
PT(EggGroup) _node_group;
|
||||
const FCDGeometryMesh *_skin_mesh;
|
||||
const FCDControllerInstance *_instance;
|
||||
LMatrix4d _bind_shape_mat;
|
||||
|
||||
private:
|
||||
std::string _name;
|
||||
const FCDSkinController *_skin_controller;
|
||||
Joints _joints;
|
||||
JointMap _bound_joints;
|
||||
|
||||
public:
|
||||
virtual TypeHandle get_type() const {
|
||||
return get_class_type();
|
||||
}
|
||||
virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
|
||||
static TypeHandle get_class_type() {
|
||||
return _type_handle;
|
||||
}
|
||||
static void init_type() {
|
||||
TypedReferenceCount::init_type();
|
||||
register_type(_type_handle, "DaeCharacter",
|
||||
TypedReferenceCount::get_class_type());
|
||||
}
|
||||
|
||||
private:
|
||||
static TypeHandle _type_handle;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,451 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file daeMaterials.cxx
|
||||
* @author rdb
|
||||
* @date 2008-10-03
|
||||
*/
|
||||
|
||||
#include "daeMaterials.h"
|
||||
#include "config_daeegg.h"
|
||||
#include "fcollada_utils.h"
|
||||
|
||||
#include <FCDocument/FCDocument.h>
|
||||
#include <FCDocument/FCDMaterial.h>
|
||||
#include <FCDocument/FCDEffect.h>
|
||||
#include <FCDocument/FCDTexture.h>
|
||||
#include <FCDocument/FCDEffectParameterSampler.h>
|
||||
#include <FCDocument/FCDImage.h>
|
||||
|
||||
#include "filename.h"
|
||||
#include "string_utils.h"
|
||||
|
||||
using std::endl;
|
||||
using std::string;
|
||||
|
||||
TypeHandle DaeMaterials::_type_handle;
|
||||
|
||||
// luminance function, based on the ISOCIE color standards see ITU-R
|
||||
// Recommendation BT.709-4
|
||||
#define luminance(c) ((c[0] * 0.212671 + c[1] * 0.715160 + c[2] * 0.072169))
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
DaeMaterials::
|
||||
DaeMaterials(const FCDGeometryInstance* geometry_instance) {
|
||||
for (size_t mi = 0; mi < geometry_instance->GetMaterialInstanceCount(); ++mi) {
|
||||
add_material_instance(geometry_instance->GetMaterialInstance(mi));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a material instance. Normally automatically done by constructor.
|
||||
*/
|
||||
void DaeMaterials::add_material_instance(const FCDMaterialInstance* instance) {
|
||||
nassertv(instance != nullptr);
|
||||
const string semantic (FROM_FSTRING(instance->GetSemantic()));
|
||||
if (_materials.count(semantic) > 0) {
|
||||
daeegg_cat.warning() << "Ignoring duplicate material with semantic " << semantic << endl;
|
||||
return;
|
||||
}
|
||||
_materials[semantic] = new DaeMaterial();
|
||||
|
||||
// Load in the uvsets
|
||||
for (size_t vib = 0; vib < instance->GetVertexInputBindingCount(); ++vib) {
|
||||
const FCDMaterialInstanceBindVertexInput* mivib = instance->GetVertexInputBinding(vib);
|
||||
assert(mivib != nullptr);
|
||||
PT(DaeVertexInputBinding) bvi = new DaeVertexInputBinding();
|
||||
bvi->_input_set = mivib->inputSet;
|
||||
#if FCOLLADA_VERSION >= 0x00030005
|
||||
bvi->_input_semantic = mivib->GetInputSemantic();
|
||||
bvi->_semantic = *mivib->semantic;
|
||||
#else
|
||||
bvi->_input_semantic = mivib->inputSemantic;
|
||||
bvi->_semantic = FROM_FSTRING(mivib->semantic);
|
||||
#endif
|
||||
_materials[semantic]->_uvsets.push_back(bvi);
|
||||
}
|
||||
|
||||
// Handle the material stuff
|
||||
if (daeegg_cat.is_spam()) {
|
||||
daeegg_cat.spam() << "Trying to process material with semantic " << semantic << endl;
|
||||
}
|
||||
PT_EggMaterial egg_material = new EggMaterial(semantic);
|
||||
pvector<PT_EggTexture> egg_textures;
|
||||
const FCDEffect* effect = instance->GetMaterial()->GetEffect();
|
||||
if (effect == nullptr) {
|
||||
if (daeegg_cat.is_debug()) {
|
||||
daeegg_cat.debug() << "Ignoring material (semantic: " << semantic << ") without assigned effect" << endl;
|
||||
}
|
||||
} else {
|
||||
// Grab the common profile effect
|
||||
const FCDEffectStandard* effect_common = (FCDEffectStandard *)effect->FindProfile(FUDaeProfileType::COMMON);
|
||||
if (effect_common == nullptr) {
|
||||
daeegg_cat.info() << "Ignoring effect referenced by material with semantic " << semantic
|
||||
<< " because it has no common profile" << endl;
|
||||
} else {
|
||||
if (daeegg_cat.is_spam()) {
|
||||
daeegg_cat.spam() << "Processing effect, material semantic is " << semantic << endl;
|
||||
}
|
||||
// Set the material parameters
|
||||
egg_material->set_amb(TO_COLOR(effect_common->GetAmbientColor()));
|
||||
// We already process transparency using blend modes LVecBase4 diffuse =
|
||||
// TO_COLOR(effect_common->GetDiffuseColor());
|
||||
// diffuse.set_w(diffuse.get_w() * (1.0f -
|
||||
// effect_common->GetOpacity())); egg_material->set_diff(diffuse);
|
||||
egg_material->set_diff(TO_COLOR(effect_common->GetDiffuseColor()));
|
||||
egg_material->set_emit(TO_COLOR(effect_common->GetEmissionColor()) * effect_common->GetEmissionFactor());
|
||||
egg_material->set_shininess(effect_common->GetShininess());
|
||||
egg_material->set_spec(TO_COLOR(effect_common->GetSpecularColor()));
|
||||
// Now try to load in the textures
|
||||
process_texture_bucket(semantic, effect_common, FUDaeTextureChannel::DIFFUSE, EggTexture::ET_modulate);
|
||||
process_texture_bucket(semantic, effect_common, FUDaeTextureChannel::BUMP, EggTexture::ET_normal);
|
||||
process_texture_bucket(semantic, effect_common, FUDaeTextureChannel::SPECULAR, EggTexture::ET_modulate_gloss);
|
||||
process_texture_bucket(semantic, effect_common, FUDaeTextureChannel::SPECULAR_LEVEL, EggTexture::ET_gloss);
|
||||
process_texture_bucket(semantic, effect_common, FUDaeTextureChannel::TRANSPARENT, EggTexture::ET_unspecified, EggTexture::F_alpha);
|
||||
process_texture_bucket(semantic, effect_common, FUDaeTextureChannel::EMISSION, EggTexture::ET_add);
|
||||
#if FCOLLADA_VERSION < 0x00030005
|
||||
process_texture_bucket(semantic, effect_common, FUDaeTextureChannel::OPACITY, EggTexture::ET_unspecified, EggTexture::F_alpha);
|
||||
#endif
|
||||
// Now, calculate the color blend stuff.
|
||||
_materials[semantic]->_blend = convert_blend(effect_common->GetTransparencyMode(),
|
||||
TO_COLOR(effect_common->GetTranslucencyColor()),
|
||||
effect_common->GetTranslucencyFactor());
|
||||
}
|
||||
// Find an <extra> tag to support some extra stuff from extensions
|
||||
process_extra(semantic, effect->GetExtra());
|
||||
}
|
||||
if (daeegg_cat.is_spam()) {
|
||||
daeegg_cat.spam() << "Found " << egg_textures.size() << " textures in material" << endl;
|
||||
}
|
||||
_materials[semantic]->_egg_material = egg_material;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the given texture bucket and gives the textures in it the given
|
||||
* envtype and format.
|
||||
*/
|
||||
void DaeMaterials::
|
||||
process_texture_bucket(const string semantic, const FCDEffectStandard* effect_common, FUDaeTextureChannel::Channel bucket, EggTexture::EnvType envtype, EggTexture::Format format) {
|
||||
for (size_t tx = 0; tx < effect_common->GetTextureCount(bucket); ++tx) {
|
||||
const FCDImage* image = effect_common->GetTexture(bucket, tx)->GetImage();
|
||||
if (image == nullptr) {
|
||||
daeegg_cat.warning() << "Texture references a nonexisting image!" << endl;
|
||||
} else {
|
||||
const FCDEffectParameterSampler* sampler = effect_common->GetTexture(bucket, tx)->GetSampler();
|
||||
// FCollada only supplies absolute paths. We need to grab the document
|
||||
// location ourselves and make the image path absolute.
|
||||
Filename texpath;
|
||||
if (image->GetDocument()) {
|
||||
Filename docpath = Filename::from_os_specific(FROM_FSTRING(image->GetDocument()->GetFileUrl()));
|
||||
docpath.make_canonical();
|
||||
texpath = Filename::from_os_specific(FROM_FSTRING(image->GetFilename()));
|
||||
texpath.make_canonical();
|
||||
texpath.make_relative_to(docpath.get_dirname(), true);
|
||||
if (daeegg_cat.is_debug()) {
|
||||
daeegg_cat.debug() << "Found texture with path " << texpath << endl;
|
||||
}
|
||||
} else {
|
||||
// Never mind.
|
||||
texpath = Filename::from_os_specific(FROM_FSTRING(image->GetFilename()));
|
||||
}
|
||||
PT_EggTexture egg_texture = new EggTexture(FROM_FSTRING(image->GetDaeId()), texpath.to_os_generic());
|
||||
// Find a set of UV coordinates
|
||||
const FCDEffectParameterInt* uvset = effect_common->GetTexture(bucket, tx)->GetSet();
|
||||
if (uvset != nullptr) {
|
||||
if (daeegg_cat.is_debug()) {
|
||||
daeegg_cat.debug() << "Texture has uv name '" << FROM_FSTRING(uvset->GetSemantic()) << "'\n";
|
||||
}
|
||||
string uvset_semantic (FROM_FSTRING(uvset->GetSemantic()));
|
||||
|
||||
// Only set the UV name if this UV set actually exists.
|
||||
for (size_t i = 0; i < _materials[semantic]->_uvsets.size(); ++i) {
|
||||
if (_materials[semantic]->_uvsets[i]->_semantic == uvset_semantic) {
|
||||
egg_texture->set_uv_name(uvset_semantic);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Apply sampler stuff
|
||||
if (sampler != nullptr) {
|
||||
egg_texture->set_texture_type(convert_texture_type(sampler->GetSamplerType()));
|
||||
egg_texture->set_wrap_u(convert_wrap_mode(sampler->GetWrapS()));
|
||||
if (sampler->GetSamplerType() != FCDEffectParameterSampler::SAMPLER1D) {
|
||||
egg_texture->set_wrap_v(convert_wrap_mode(sampler->GetWrapT()));
|
||||
}
|
||||
if (sampler->GetSamplerType() == FCDEffectParameterSampler::SAMPLER3D) {
|
||||
egg_texture->set_wrap_w(convert_wrap_mode(sampler->GetWrapP()));
|
||||
}
|
||||
egg_texture->set_minfilter(convert_filter_type(sampler->GetMinFilter()));
|
||||
egg_texture->set_magfilter(convert_filter_type(sampler->GetMagFilter()));
|
||||
if (envtype != EggTexture::ET_unspecified) {
|
||||
egg_texture->set_env_type(envtype);
|
||||
}
|
||||
if (format != EggTexture::F_unspecified) {
|
||||
egg_texture->set_format(format);
|
||||
}
|
||||
}
|
||||
_materials[semantic]->_egg_textures.push_back(egg_texture);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the extra data in the given <extra> tag. If the given element is
|
||||
* NULL, it just silently returns.
|
||||
*/
|
||||
void DaeMaterials::
|
||||
process_extra(const string semantic, const FCDExtra* extra) {
|
||||
if (extra == nullptr) return;
|
||||
const FCDEType* etype = extra->GetDefaultType();
|
||||
if (etype == nullptr) return;
|
||||
for (size_t et = 0; et < etype->GetTechniqueCount(); ++et) {
|
||||
const FCDENode* enode = ((const FCDENode*)(etype->GetTechnique(et)))->FindChildNode("double_sided");
|
||||
if (enode != nullptr) {
|
||||
string content = trim(enode->GetContent());
|
||||
if (content == "1" || content == "true") {
|
||||
_materials[semantic]->_double_sided = true;
|
||||
} else if (content == "0" || content == "false") {
|
||||
_materials[semantic]->_double_sided = false;
|
||||
} else {
|
||||
daeegg_cat.warning() << "Expected <double_sided> tag to be either 1 or 0, found '" << content << "' instead" << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the stuff to the given EggPrimitive.
|
||||
*/
|
||||
void DaeMaterials::
|
||||
apply_to_primitive(const string semantic, const PT(EggPrimitive) to) {
|
||||
if (_materials.count(semantic) > 0) {
|
||||
to->set_material(_materials[semantic]->_egg_material);
|
||||
for (pvector<PT_EggTexture>::iterator it = _materials[semantic]->_egg_textures.begin(); it != _materials[semantic]->_egg_textures.end(); ++it) {
|
||||
if (daeegg_cat.is_spam()) {
|
||||
daeegg_cat.spam() << "Applying texture " << (*it)->get_name() << " from material with semantic " << semantic << endl;
|
||||
}
|
||||
to->add_texture(*it);
|
||||
}
|
||||
to->set_bface_flag(_materials[semantic]->_double_sided);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the colorblend stuff to the given EggGroup.
|
||||
*/
|
||||
void DaeMaterials::
|
||||
apply_to_group(const string semantic, const PT(EggGroup) to, bool invert_transparency) {
|
||||
if (_materials.count(semantic) > 0) {
|
||||
PT(DaeBlendSettings) blend = _materials[semantic]->_blend;
|
||||
if (blend && blend->_enabled) {
|
||||
to->set_blend_mode(EggGroup::BM_add);
|
||||
to->set_blend_color(blend->_color);
|
||||
if (invert_transparency) {
|
||||
to->set_blend_operand_a(blend->_operand_b);
|
||||
to->set_blend_operand_b(blend->_operand_a);
|
||||
} else {
|
||||
to->set_blend_operand_a(blend->_operand_a);
|
||||
to->set_blend_operand_b(blend->_operand_b);
|
||||
}
|
||||
} else if (blend && invert_transparency) {
|
||||
to->set_blend_mode(EggGroup::BM_add);
|
||||
to->set_blend_color(blend->_color);
|
||||
to->set_blend_operand_a(blend->_operand_b);
|
||||
to->set_blend_operand_b(blend->_operand_a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the semantic of the uvset with the specified input set, or an empty
|
||||
* string if the given material has no input set.
|
||||
*/
|
||||
const string DaeMaterials::
|
||||
get_uvset_name(const string semantic, FUDaeGeometryInput::Semantic input_semantic, int32 input_set) {
|
||||
if (_materials.count(semantic) > 0) {
|
||||
if (input_set == -1 && _materials[semantic]->_uvsets.size() == 1) {
|
||||
return _materials[semantic]->_uvsets[0]->_semantic;
|
||||
} else {
|
||||
for (size_t i = 0; i < _materials[semantic]->_uvsets.size(); ++i) {
|
||||
if (_materials[semantic]->_uvsets[i]->_input_set == input_set &&
|
||||
_materials[semantic]->_uvsets[i]->_input_semantic == input_semantic) {
|
||||
return _materials[semantic]->_uvsets[i]->_semantic;
|
||||
}
|
||||
}
|
||||
// If we can't find it, let's look again, but don't care for the
|
||||
// input_semantic this time. The reason for this is that some tools
|
||||
// export textangents and texbinormals bound to a uvset with input
|
||||
// semantic TEXCOORD.
|
||||
for (size_t i = 0; i < _materials[semantic]->_uvsets.size(); ++i) {
|
||||
if (_materials[semantic]->_uvsets[i]->_input_set == input_set) {
|
||||
if (daeegg_cat.is_debug()) {
|
||||
daeegg_cat.debug() << "Using uv set with non-matching input semantic " << _materials[semantic]->_uvsets[i]->_semantic << "\n";
|
||||
}
|
||||
return _materials[semantic]->_uvsets[i]->_semantic;
|
||||
}
|
||||
}
|
||||
if (daeegg_cat.is_debug()) {
|
||||
daeegg_cat.debug() << "No uv set binding found for input set " << input_set << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an FCollada sampler type to the EggTexture texture type
|
||||
* equivalent.
|
||||
*/
|
||||
EggTexture::TextureType DaeMaterials::
|
||||
convert_texture_type(const FCDEffectParameterSampler::SamplerType orig_type) {
|
||||
switch (orig_type) {
|
||||
case FCDEffectParameterSampler::SAMPLER1D:
|
||||
return EggTexture::TT_1d_texture;
|
||||
case FCDEffectParameterSampler::SAMPLER2D:
|
||||
return EggTexture::TT_2d_texture;
|
||||
case FCDEffectParameterSampler::SAMPLER3D:
|
||||
return EggTexture::TT_3d_texture;
|
||||
case FCDEffectParameterSampler::SAMPLERCUBE:
|
||||
return EggTexture::TT_cube_map;
|
||||
default:
|
||||
daeegg_cat.warning() << "Invalid sampler type found" << endl;
|
||||
}
|
||||
return EggTexture::TT_unspecified;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an FCollada wrap mode to the EggTexture wrap mode equivalent.
|
||||
*/
|
||||
EggTexture::WrapMode DaeMaterials::
|
||||
convert_wrap_mode(const FUDaeTextureWrapMode::WrapMode orig_mode) {
|
||||
switch (orig_mode) {
|
||||
case FUDaeTextureWrapMode::NONE:
|
||||
// FIXME: this shouldnt be unspecified
|
||||
return EggTexture::WM_unspecified;
|
||||
case FUDaeTextureWrapMode::WRAP:
|
||||
return EggTexture::WM_repeat;
|
||||
case FUDaeTextureWrapMode::MIRROR:
|
||||
return EggTexture::WM_mirror;
|
||||
case FUDaeTextureWrapMode::CLAMP:
|
||||
return EggTexture::WM_clamp;
|
||||
case FUDaeTextureWrapMode::BORDER:
|
||||
return EggTexture::WM_border_color;
|
||||
case FUDaeTextureWrapMode::UNKNOWN:
|
||||
return EggTexture::WM_unspecified;
|
||||
default:
|
||||
daeegg_cat.warning() << "Invalid wrap mode found: " << FUDaeTextureWrapMode::ToString(orig_mode) << endl;
|
||||
}
|
||||
return EggTexture::WM_unspecified;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an FCollada filter function to the EggTexture wrap type
|
||||
* equivalent.
|
||||
*/
|
||||
EggTexture::FilterType DaeMaterials::
|
||||
convert_filter_type(const FUDaeTextureFilterFunction::FilterFunction orig_type) {
|
||||
switch (orig_type) {
|
||||
case FUDaeTextureFilterFunction::NONE:
|
||||
// FIXME: this shouldnt be unspecified
|
||||
return EggTexture::FT_unspecified;
|
||||
case FUDaeTextureFilterFunction::NEAREST:
|
||||
return EggTexture::FT_nearest;
|
||||
case FUDaeTextureFilterFunction::LINEAR:
|
||||
return EggTexture::FT_linear;
|
||||
case FUDaeTextureFilterFunction::NEAREST_MIPMAP_NEAREST:
|
||||
return EggTexture::FT_nearest_mipmap_nearest;
|
||||
case FUDaeTextureFilterFunction::LINEAR_MIPMAP_NEAREST:
|
||||
return EggTexture::FT_linear_mipmap_nearest;
|
||||
case FUDaeTextureFilterFunction::NEAREST_MIPMAP_LINEAR:
|
||||
return EggTexture::FT_nearest_mipmap_linear;
|
||||
case FUDaeTextureFilterFunction::LINEAR_MIPMAP_LINEAR:
|
||||
return EggTexture::FT_linear_mipmap_linear;
|
||||
case FUDaeTextureFilterFunction::UNKNOWN:
|
||||
return EggTexture::FT_unspecified;
|
||||
default:
|
||||
daeegg_cat.warning() << "Unknown filter type found: " << FUDaeTextureFilterFunction::ToString(orig_type) << endl;
|
||||
}
|
||||
return EggTexture::FT_unspecified;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts collada blend attribs to Panda's equivalents.
|
||||
*/
|
||||
PT(DaeMaterials::DaeBlendSettings) DaeMaterials::
|
||||
convert_blend(FCDEffectStandard::TransparencyMode mode, const LColor &transparent, double transparency) {
|
||||
// Create the DaeBlendSettings and fill it with some defaults.
|
||||
PT(DaeBlendSettings) blend = new DaeBlendSettings();
|
||||
blend->_enabled = true;
|
||||
blend->_color = LColor::zero();
|
||||
blend->_operand_a = EggGroup::BO_unspecified;
|
||||
blend->_operand_b = EggGroup::BO_unspecified;
|
||||
|
||||
// First fill in the color value.
|
||||
if (mode == FCDEffectStandard::A_ONE) {// || mode == FCDEffectStandard::A_ZERO) {
|
||||
double value = transparent[3] * transparency;
|
||||
blend->_color = LColor(value, value, value, value);
|
||||
} else if (mode == FCDEffectStandard::RGB_ZERO) {//|| mode == FCDEffectStandard::RGB_ONE) {
|
||||
blend->_color = transparent * transparency;
|
||||
blend->_color[3] = luminance(blend->_color);
|
||||
} else {
|
||||
daeegg_cat.error() << "Unknown opaque type found!" << endl;
|
||||
blend->_enabled = false;
|
||||
return blend;
|
||||
}
|
||||
|
||||
// Now figure out the operands.
|
||||
if (mode == FCDEffectStandard::RGB_ZERO) {// || mode == FCDEffectStandard::A_ZERO) {
|
||||
blend->_operand_a = EggGroup::BO_one_minus_constant_color;
|
||||
blend->_operand_b = EggGroup::BO_constant_color;
|
||||
} else if (mode == FCDEffectStandard::A_ONE) {// || mode == FCDEffectStandard::RGB_ONE) {
|
||||
blend->_operand_a = EggGroup::BO_constant_color;
|
||||
blend->_operand_b = EggGroup::BO_one_minus_constant_color;
|
||||
} else {
|
||||
daeegg_cat.error() << "Unknown opaque type found!" << endl;
|
||||
blend->_enabled = false;
|
||||
return blend;
|
||||
}
|
||||
|
||||
// See if we can optimize out the color.
|
||||
if (blend->_operand_a == EggGroup::BO_constant_color) {
|
||||
if (blend->_color == LColor::zero()) {
|
||||
blend->_operand_a = EggGroup::BO_zero;
|
||||
} else if (blend->_color == LColor(1, 1, 1, 1)) {
|
||||
blend->_operand_a = EggGroup::BO_one;
|
||||
}
|
||||
}
|
||||
if (blend->_operand_b == EggGroup::BO_constant_color) {
|
||||
if (blend->_color == LColor::zero()) {
|
||||
blend->_operand_b = EggGroup::BO_zero;
|
||||
} else if (blend->_color == LColor(1, 1, 1, 1)) {
|
||||
blend->_operand_b = EggGroup::BO_one;
|
||||
}
|
||||
}
|
||||
if (blend->_operand_a == EggGroup::BO_one_minus_constant_color) {
|
||||
if (blend->_color == LColor::zero()) {
|
||||
blend->_operand_a = EggGroup::BO_one;
|
||||
} else if (blend->_color == LColor(1, 1, 1, 1)) {
|
||||
blend->_operand_a = EggGroup::BO_zero;
|
||||
}
|
||||
}
|
||||
if (blend->_operand_b == EggGroup::BO_one_minus_constant_color) {
|
||||
if (blend->_color == LColor::zero()) {
|
||||
blend->_operand_b = EggGroup::BO_one;
|
||||
} else if (blend->_color == LColor(1, 1, 1, 1)) {
|
||||
blend->_operand_b = EggGroup::BO_zero;
|
||||
}
|
||||
}
|
||||
|
||||
// See if we can entirely disable the blend.
|
||||
if (blend->_operand_a == EggGroup::BO_one && blend->_operand_b == EggGroup::BO_zero) {
|
||||
blend->_enabled = false;
|
||||
}
|
||||
return blend;
|
||||
}
|
||||
@ -1,101 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file daeMaterials.h
|
||||
* @author rdb
|
||||
* @date 2008-10-03
|
||||
*/
|
||||
|
||||
#ifndef DAEMATERIALS_H
|
||||
#define DAEMATERIALS_H
|
||||
|
||||
#include "pandatoolbase.h"
|
||||
#include "eggMaterial.h"
|
||||
#include "eggTexture.h"
|
||||
#include "eggPrimitive.h"
|
||||
#include "eggGroup.h"
|
||||
#include "pointerTo.h"
|
||||
#include "pt_EggTexture.h"
|
||||
#include "pt_EggMaterial.h"
|
||||
|
||||
#include "pre_fcollada_include.h"
|
||||
#include <FCollada.h>
|
||||
#include <FCDocument/FCDGeometryInstance.h>
|
||||
#include <FCDocument/FCDMaterialInstance.h>
|
||||
#include <FCDocument/FCDEffectStandard.h>
|
||||
#include <FCDocument/FCDEffectParameterSampler.h>
|
||||
#include <FCDocument/FCDExtra.h>
|
||||
|
||||
/**
|
||||
* This class is seperated from the converter file because otherwise it would
|
||||
* get too big and needlessly complicated.
|
||||
*/
|
||||
class DaeMaterials : public TypedReferenceCount {
|
||||
public:
|
||||
DaeMaterials(const FCDGeometryInstance* geometry_instance);
|
||||
virtual ~DaeMaterials() {};
|
||||
|
||||
void add_material_instance(const FCDMaterialInstance* instance);
|
||||
void apply_to_primitive(const std::string semantic, const PT(EggPrimitive) to);
|
||||
void apply_to_group(const std::string semantic, const PT(EggGroup) to, bool invert_transparency=false);
|
||||
const std::string get_uvset_name(const std::string semantic, FUDaeGeometryInput::Semantic input_semantic, int32 input_set);
|
||||
|
||||
static EggTexture::TextureType convert_texture_type(const FCDEffectParameterSampler::SamplerType orig_type);
|
||||
static EggTexture::WrapMode convert_wrap_mode(const FUDaeTextureWrapMode::WrapMode orig_mode);
|
||||
static EggTexture::FilterType convert_filter_type(const FUDaeTextureFilterFunction::FilterFunction orig_type);
|
||||
|
||||
private:
|
||||
// Holds stuff for color blend attribs.
|
||||
struct DaeBlendSettings : public ReferenceCount {
|
||||
bool _enabled;
|
||||
LColor _color;
|
||||
EggGroup::BlendOperand _operand_a;
|
||||
EggGroup::BlendOperand _operand_b;
|
||||
};
|
||||
|
||||
// Holds information to bind texcoord inputs to textures.
|
||||
struct DaeVertexInputBinding : public ReferenceCount {
|
||||
int32 _input_set;
|
||||
FUDaeGeometryInput::Semantic _input_semantic;
|
||||
std::string _semantic;
|
||||
};
|
||||
|
||||
// Holds stuff for an individual material.
|
||||
struct DaeMaterial : public ReferenceCount {
|
||||
pvector<PT_EggTexture> _egg_textures;
|
||||
PT_EggMaterial _egg_material;
|
||||
bool _double_sided;
|
||||
pvector<PT(DaeVertexInputBinding)> _uvsets;
|
||||
PT(DaeBlendSettings) _blend;
|
||||
};
|
||||
|
||||
void process_texture_bucket(const std::string semantic, const FCDEffectStandard* effect_common, FUDaeTextureChannel::Channel bucket, EggTexture::EnvType envtype = EggTexture::ET_unspecified, EggTexture::Format format = EggTexture::F_unspecified);
|
||||
void process_extra(const std::string semantic, const FCDExtra* extra);
|
||||
static PT(DaeBlendSettings) convert_blend(FCDEffectStandard::TransparencyMode mode, const LColor &transparent, double transparency);
|
||||
|
||||
pmap<const std::string, PT(DaeMaterial)> _materials;
|
||||
|
||||
public:
|
||||
virtual TypeHandle get_type() const {
|
||||
return get_class_type();
|
||||
}
|
||||
virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
|
||||
static TypeHandle get_class_type() {
|
||||
return _type_handle;
|
||||
}
|
||||
static void init_type() {
|
||||
TypedReferenceCount::init_type();
|
||||
register_type(_type_handle, "DaeMaterials",
|
||||
TypedReferenceCount::get_class_type());
|
||||
}
|
||||
|
||||
private:
|
||||
static TypeHandle _type_handle;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,851 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file daeToEggConverter.cxx
|
||||
* @author rdb
|
||||
* @date 2008-05-08
|
||||
*/
|
||||
|
||||
#include "daeToEggConverter.h"
|
||||
#include "fcollada_utils.h"
|
||||
#include "config_daeegg.h"
|
||||
#include "daeCharacter.h"
|
||||
#include "dcast.h"
|
||||
#include "string_utils.h"
|
||||
#include "eggData.h"
|
||||
#include "eggPrimitive.h"
|
||||
#include "eggLine.h"
|
||||
#include "eggPolygon.h"
|
||||
#include "eggTriangleFan.h"
|
||||
#include "eggTriangleStrip.h"
|
||||
#include "eggPoint.h"
|
||||
#include "eggXfmSAnim.h"
|
||||
#include "eggSAnimData.h"
|
||||
#include "pt_EggVertex.h"
|
||||
|
||||
#include <FCDocument/FCDAsset.h>
|
||||
#include <FCDocument/FCDocumentTools.h>
|
||||
#include <FCDocument/FCDSceneNode.h>
|
||||
#include <FCDocument/FCDSceneNodeTools.h>
|
||||
#include <FCDocument/FCDGeometry.h>
|
||||
#include <FCDocument/FCDGeometryInstance.h>
|
||||
#include <FCDocument/FCDGeometryPolygons.h>
|
||||
#include <FCDocument/FCDGeometrySource.h>
|
||||
#include <FCDocument/FCDSkinController.h>
|
||||
#include <FCDocument/FCDController.h>
|
||||
#include <FCDocument/FCDControllerInstance.h>
|
||||
#include <FCDocument/FCDMorphController.h>
|
||||
#include <FCDocument/FCDMaterialInstance.h>
|
||||
#include <FCDocument/FCDExtra.h>
|
||||
#include <FCDocument/FCDEffect.h>
|
||||
#include <FCDocument/FCDEffectStandard.h>
|
||||
#if FCOLLADA_VERSION >= 0x00030005
|
||||
#include <FCDocument/FCDGeometryPolygonsInput.h>
|
||||
#endif
|
||||
|
||||
using std::endl;
|
||||
using std::string;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
DAEToEggConverter::
|
||||
DAEToEggConverter() {
|
||||
_unit_name = "meter";
|
||||
_unit_meters = 1.0;
|
||||
_document = nullptr;
|
||||
_table = nullptr;
|
||||
_error_handler = nullptr;
|
||||
_invert_transparency = false;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
DAEToEggConverter::
|
||||
DAEToEggConverter(const DAEToEggConverter ©) :
|
||||
SomethingToEggConverter(copy)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
DAEToEggConverter::
|
||||
~DAEToEggConverter() {
|
||||
delete _error_handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocates and returns a new copy of the converter.
|
||||
*/
|
||||
SomethingToEggConverter *DAEToEggConverter::
|
||||
make_copy() {
|
||||
return new DAEToEggConverter(*this);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the English name of the file type this converter supports.
|
||||
*/
|
||||
string DAEToEggConverter::
|
||||
get_name() const {
|
||||
return "COLLADA";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the common extension of the file type this converter supports.
|
||||
*/
|
||||
string DAEToEggConverter::
|
||||
get_extension() const {
|
||||
return "dae";
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the reading of the input file and converting it to egg. Returns
|
||||
* true if successful, false otherwise.
|
||||
*/
|
||||
bool DAEToEggConverter::
|
||||
convert_file(const Filename &filename) {
|
||||
// Reset stuff
|
||||
clear_error();
|
||||
_joints.clear();
|
||||
if (_error_handler == nullptr) {
|
||||
_error_handler = new FUErrorSimpleHandler;
|
||||
}
|
||||
|
||||
// The default coordinate system is Y-up
|
||||
if (_egg_data->get_coordinate_system() == CS_default) {
|
||||
_egg_data->set_coordinate_system(CS_yup_right);
|
||||
}
|
||||
|
||||
// Read the file
|
||||
FCollada::Initialize();
|
||||
_document = FCollada::LoadDocument(filename.to_os_specific().c_str());
|
||||
if (_document == nullptr) {
|
||||
daeegg_cat.error() << "Failed to load document: " << _error_handler->GetErrorString() << endl;
|
||||
FCollada::Release();
|
||||
return false;
|
||||
}
|
||||
// Make sure the file uses consistent coordinate system and length
|
||||
if (_document->GetAsset() != nullptr) {
|
||||
FCDocumentTools::StandardizeUpAxisAndLength(_document);
|
||||
}
|
||||
|
||||
// Process the scene
|
||||
process_asset();
|
||||
PT(EggGroup) scene_group;
|
||||
string model_name = _character_name;
|
||||
|
||||
FCDSceneNode* visual_scene = _document->GetVisualSceneInstance();
|
||||
if (visual_scene != nullptr) {
|
||||
if (model_name.empty()) {
|
||||
// By lack of anything better...
|
||||
model_name = FROM_FSTRING(visual_scene->GetName());
|
||||
}
|
||||
scene_group = new EggGroup(model_name);
|
||||
_egg_data->add_child(scene_group);
|
||||
|
||||
for (size_t ch = 0; ch < visual_scene->GetChildrenCount(); ++ch) {
|
||||
process_node(scene_group, visual_scene->GetChild(ch));
|
||||
}
|
||||
} else {
|
||||
daeegg_cat.warning()
|
||||
<< "No visual scene instance found in COLLADA document.\n";
|
||||
}
|
||||
|
||||
// Now process the characters. This depends on information from collected
|
||||
// joints, which is why it's done in a second step.
|
||||
if (get_animation_convert() != AC_none) {
|
||||
Characters::iterator it;
|
||||
DaeCharacter *character;
|
||||
for (it = _characters.begin(); it != _characters.end(); ++it) {
|
||||
character = *it;
|
||||
if (get_animation_convert() != AC_chan) {
|
||||
character->bind_joints(_joints);
|
||||
|
||||
const FCDGeometryMesh *mesh = character->_skin_mesh;
|
||||
|
||||
if (mesh != nullptr) {
|
||||
PT(DaeMaterials) materials = new DaeMaterials(character->_instance);
|
||||
if (daeegg_cat.is_spam()) {
|
||||
daeegg_cat.spam() << "Processing mesh for controller\n";
|
||||
}
|
||||
process_mesh(character->_node_group, mesh, materials, character);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Put the joints in bind pose.
|
||||
for (size_t ch = 0; ch < visual_scene->GetChildrenCount(); ++ch) {
|
||||
character->adjust_joints(visual_scene->GetChild(ch), _joints, LMatrix4d::ident_mat());
|
||||
}
|
||||
|
||||
if (scene_group != nullptr) {
|
||||
// Mark the scene as character.
|
||||
if (get_animation_convert() == AC_chan) {
|
||||
_egg_data->remove_child(scene_group);
|
||||
} else {
|
||||
scene_group->set_dart_type(EggGroup::DT_default);
|
||||
}
|
||||
}
|
||||
|
||||
if (get_animation_convert() != AC_model) {
|
||||
_table = new EggTable();
|
||||
_table->set_table_type(EggTable::TT_table);
|
||||
_egg_data->add_child(_table);
|
||||
|
||||
PT(EggTable) bundle = new EggTable(model_name);
|
||||
bundle->set_table_type(EggTable::TT_bundle);
|
||||
_table->add_child(bundle);
|
||||
|
||||
PT(EggTable) skeleton = new EggTable("<skeleton>");
|
||||
skeleton->set_table_type(EggTable::TT_table);
|
||||
bundle->add_child(skeleton);
|
||||
|
||||
pset<float> keys;
|
||||
|
||||
Characters::iterator it;
|
||||
DaeCharacter *character;
|
||||
for (it = _characters.begin(); it != _characters.end(); ++it) {
|
||||
character = *it;
|
||||
|
||||
// Collect key frame timings.
|
||||
if (get_animation_convert() == AC_both ||
|
||||
get_animation_convert() == AC_chan) {
|
||||
character->collect_keys(keys);
|
||||
}
|
||||
}
|
||||
|
||||
if (_frame_inc != 0.0) {
|
||||
// A frame increment was given, this means that we have to sample the
|
||||
// animation.
|
||||
float start, end;
|
||||
if (_end_frame != _start_frame) {
|
||||
start = _start_frame;
|
||||
end = _end_frame;
|
||||
} else {
|
||||
// No range was given. Infer the frame range from the keys.
|
||||
start = *keys.begin();
|
||||
end = *keys.rbegin();
|
||||
}
|
||||
keys.clear();
|
||||
|
||||
for (float t = start; t <= end; t += _frame_inc) {
|
||||
keys.insert(t);
|
||||
}
|
||||
} else {
|
||||
// No sampling parameters given; not necessarily a failure, since the
|
||||
// animation may already be sampled. We use the key frames as
|
||||
// animation frames.
|
||||
if (_end_frame != 0.0) {
|
||||
// An end frame was given, chop off all keys after that.
|
||||
float end = _end_frame;
|
||||
pset<float>::iterator ki;
|
||||
for (ki = keys.begin(); ki != keys.end(); ++ki) {
|
||||
if (*ki > end && !IS_THRESHOLD_EQUAL(*ki, end, 0.001)) {
|
||||
keys.erase(ki, keys.end());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_start_frame != 0.0) {
|
||||
// A start frame was given, chop off all keys before that.
|
||||
float start = _start_frame;
|
||||
pset<float>::iterator ki;
|
||||
for (ki = keys.begin(); ki != keys.end(); ++ki) {
|
||||
if (*ki > start && !IS_THRESHOLD_EQUAL(*ki, start, 0.001)) {
|
||||
keys.erase(keys.begin(), ki);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check that this does indeed look like a sampled animation; if not,
|
||||
// issue an appropriate warning.
|
||||
pset<float>::const_iterator ki = keys.begin();
|
||||
if (ki != keys.end()) {
|
||||
float last = *ki;
|
||||
float diff = 0;
|
||||
|
||||
for (++ki; ki != keys.end(); ++ki) {
|
||||
if (diff != 0 && !IS_THRESHOLD_EQUAL((*ki - last), diff, 0.001)) {
|
||||
daeegg_cat.error()
|
||||
<< "This does not appear to be a sampled animation.\n"
|
||||
<< "Specify the -sf, -ef and -if options to indicate how the "
|
||||
<< "animations should be sampled.\n";
|
||||
break;
|
||||
}
|
||||
diff = (*ki - last);
|
||||
last = *ki;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// It doesn't really matter which character we grab for this as it'll
|
||||
// iterate over the whole graph right now anyway.
|
||||
for (size_t ch = 0; ch < visual_scene->GetChildrenCount(); ++ch) {
|
||||
character->build_table(skeleton, visual_scene->GetChild(ch), keys);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up and return
|
||||
SAFE_DELETE(visual_scene);
|
||||
SAFE_DELETE(_document);
|
||||
FCollada::Release();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 DAEToEggConverter::
|
||||
get_input_units() {
|
||||
if (IS_NEARLY_EQUAL(_unit_meters, 0.001)) {
|
||||
return DU_millimeters;
|
||||
}
|
||||
if (IS_NEARLY_EQUAL(_unit_meters, 0.01)) {
|
||||
return DU_centimeters;
|
||||
}
|
||||
if (IS_NEARLY_EQUAL(_unit_meters, 1.0)) {
|
||||
return DU_meters;
|
||||
}
|
||||
if (IS_NEARLY_EQUAL(_unit_meters, 1000.0)) {
|
||||
return DU_kilometers;
|
||||
}
|
||||
if (IS_NEARLY_EQUAL(_unit_meters, 3.0 * 12.0 * 0.0254)) {
|
||||
return DU_yards;
|
||||
}
|
||||
if (IS_NEARLY_EQUAL(_unit_meters, 12.0 * 0.0254)) {
|
||||
return DU_feet;
|
||||
}
|
||||
if (IS_NEARLY_EQUAL(_unit_meters, 0.0254)) {
|
||||
return DU_inches;
|
||||
}
|
||||
if (IS_NEARLY_EQUAL(_unit_meters, 1852.0)) {
|
||||
return DU_nautical_miles;
|
||||
}
|
||||
if (IS_NEARLY_EQUAL(_unit_meters, 5280.0 * 12.0 * 0.0254)) {
|
||||
return DU_statute_miles;
|
||||
}
|
||||
|
||||
// Whatever.
|
||||
return DU_invalid;
|
||||
}
|
||||
|
||||
void DAEToEggConverter::
|
||||
process_asset() {
|
||||
const FCDAsset *asset = _document->GetAsset();
|
||||
if (_document->GetAsset() == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
_unit_name = FROM_FSTRING(asset->GetUnitName());
|
||||
_unit_meters = asset->GetUnitConversionFactor();
|
||||
|
||||
// Read out the coordinate system
|
||||
FMVector3 up_axis = asset->GetUpAxis();
|
||||
|
||||
if (up_axis == FMVector3(0, 1, 0)) {
|
||||
_egg_data->set_coordinate_system(CS_yup_right);
|
||||
|
||||
} else if (up_axis == FMVector3(0, 0, 1)) {
|
||||
_egg_data->set_coordinate_system(CS_zup_right);
|
||||
|
||||
} else {
|
||||
_egg_data->set_coordinate_system(CS_invalid);
|
||||
daeegg_cat.warning() << "Unrecognized coordinate system!\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Process the node. If forced is true, it will even process it if its known
|
||||
// to be a skeleton root.
|
||||
void DAEToEggConverter::
|
||||
process_node(EggGroupNode *parent, const FCDSceneNode* node, bool forced) {
|
||||
nassertv(node != nullptr);
|
||||
string node_id = FROM_FSTRING(node->GetDaeId());
|
||||
if (daeegg_cat.is_spam()) {
|
||||
daeegg_cat.spam() << "Processing node with ID '" << node_id << "'" << endl;
|
||||
}
|
||||
|
||||
// Create an egg group for this node
|
||||
PT(EggGroup) node_group = new EggGroup(FROM_FSTRING(node->GetDaeId()));
|
||||
process_extra(node_group, node->GetExtra());
|
||||
parent->add_child(node_group);
|
||||
|
||||
// Check if its a joint
|
||||
if (node->IsJoint()) {
|
||||
string sid = FROM_FSTRING(node->GetSubId());
|
||||
node_group->set_group_type(EggGroup::GT_joint);
|
||||
|
||||
if (!_joints.insert(DaeCharacter::JointMap::value_type(sid,
|
||||
DaeCharacter::Joint(node_group, node))).second) {
|
||||
daeegg_cat.error()
|
||||
<< "Joint with sid " << sid << " occurs more than once!\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Loop through the transforms and apply them (in reverse order)
|
||||
for (size_t tr = node->GetTransformCount(); tr > 0; --tr) {
|
||||
apply_transform(node_group, node->GetTransform(tr - 1));
|
||||
}
|
||||
// node_group->set_transform3d(convert_matrix(node->ToMatrix()));
|
||||
|
||||
// Loop through the instances and process them
|
||||
for (size_t in = 0; in < node->GetInstanceCount(); ++in) {
|
||||
process_instance(node_group, node->GetInstance(in));
|
||||
}
|
||||
|
||||
// Loop through the children and recursively process them
|
||||
for (size_t ch = 0; ch < node->GetChildrenCount(); ++ch) {
|
||||
process_node(DCAST(EggGroupNode, node_group), node->GetChild(ch));
|
||||
}
|
||||
|
||||
// Loop through any possible scene node instances and process those, too.
|
||||
for (size_t in = 0; in < node->GetInstanceCount(); ++in) {
|
||||
const FCDEntity *entity = node->GetInstance(in)->GetEntity();
|
||||
if (entity && entity->GetType() == FCDEntity::SCENE_NODE) {
|
||||
process_node(node_group, (const FCDSceneNode*) entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DAEToEggConverter::
|
||||
process_instance(EggGroup *parent, const FCDEntityInstance* instance) {
|
||||
nassertv(instance != nullptr);
|
||||
nassertv(instance->GetEntity() != nullptr);
|
||||
// Check what kind of instance this is
|
||||
switch (instance->GetType()) {
|
||||
case FCDEntityInstance::GEOMETRY:
|
||||
{
|
||||
if (get_animation_convert() != AC_chan) {
|
||||
const FCDGeometry* geometry = (const FCDGeometry*) instance->GetEntity();
|
||||
assert(geometry != nullptr);
|
||||
if (geometry->IsMesh()) {
|
||||
// Now, handle the mesh.
|
||||
process_mesh(parent, geometry->GetMesh(), new DaeMaterials((const FCDGeometryInstance*) instance));
|
||||
}
|
||||
if (geometry->IsSpline()) {
|
||||
process_spline(parent, FROM_FSTRING(geometry->GetName()), const_cast<FCDGeometrySpline*> (geometry->GetSpline()));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case FCDEntityInstance::CONTROLLER:
|
||||
// Add the dart tag and process the controller instance
|
||||
// parent->set_dart_type(EggGroup::DT_default);
|
||||
process_controller(parent, (const FCDControllerInstance*) instance);
|
||||
break;
|
||||
|
||||
case FCDEntityInstance::MATERIAL:
|
||||
// We don't process this directly, handled per-geometry instead.
|
||||
break;
|
||||
|
||||
case FCDEntityInstance::SIMPLE:
|
||||
{
|
||||
// Grab the entity and check its type.
|
||||
const FCDEntity* entity = instance->GetEntity();
|
||||
if (entity->GetType() != FCDEntity::SCENE_NODE) {
|
||||
daeegg_cat.warning() << "Unsupported entity type found" << endl;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
daeegg_cat.warning() << "Unsupported instance type found" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
// Processes the given mesh.
|
||||
void DAEToEggConverter::
|
||||
process_mesh(EggGroup *parent, const FCDGeometryMesh* mesh,
|
||||
DaeMaterials *materials, DaeCharacter *character) {
|
||||
|
||||
nassertv(mesh != nullptr);
|
||||
if (daeegg_cat.is_debug()) {
|
||||
daeegg_cat.debug() << "Processing mesh with id " << FROM_FSTRING(mesh->GetDaeId()) << endl;
|
||||
}
|
||||
|
||||
// Create the egg stuff to hold this mesh
|
||||
PT(EggGroup) mesh_group = new EggGroup(FROM_FSTRING(mesh->GetDaeId()));
|
||||
parent->add_child(mesh_group);
|
||||
PT(EggVertexPool) mesh_pool = new EggVertexPool(FROM_FSTRING(mesh->GetDaeId()));
|
||||
mesh_group->add_child(mesh_pool);
|
||||
|
||||
// First retrieve the vertex source
|
||||
if (mesh->GetSourceCount() == 0) {
|
||||
if (daeegg_cat.is_debug()) {
|
||||
daeegg_cat.debug() << "Mesh with id " << FROM_FSTRING(mesh->GetDaeId()) << " has no sources" << endl;
|
||||
}
|
||||
return;
|
||||
}
|
||||
const FCDGeometrySource* vsource = mesh->FindSourceByType(FUDaeGeometryInput::POSITION);
|
||||
if (vsource == nullptr) {
|
||||
if (daeegg_cat.is_debug()) {
|
||||
daeegg_cat.debug() << "Mesh with id " << FROM_FSTRING(mesh->GetDaeId()) << " has no source for POSITION data" << endl;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Loop through the polygon groups and add them
|
||||
if (daeegg_cat.is_spam()) {
|
||||
daeegg_cat.spam() << "Mesh with id " << FROM_FSTRING(mesh->GetDaeId()) << " has " << mesh->GetPolygonsCount() << " polygon groups" << endl;
|
||||
}
|
||||
if (mesh->GetPolygonsCount() == 0) return;
|
||||
|
||||
// This is an array of pointers, I know. But since they are refcounted, I
|
||||
// don't have a better idea.
|
||||
PT(EggGroup) *primitive_holders = new PT(EggGroup) [mesh->GetPolygonsCount()];
|
||||
for (size_t gr = 0; gr < mesh->GetPolygonsCount(); ++gr) {
|
||||
const FCDGeometryPolygons* polygons = mesh->GetPolygons(gr);
|
||||
string material_semantic = FROM_FSTRING(polygons->GetMaterialSemantic());
|
||||
|
||||
// Stores which group holds the primitives.
|
||||
PT(EggGroup) primitiveholder;
|
||||
// If we have materials, make a group for each material. Then, apply the
|
||||
// material's per-group stuff.
|
||||
if (materials != nullptr && (!polygons->GetMaterialSemantic().empty()) && mesh->GetPolygonsCount() > 1) {
|
||||
// primitiveholder = new EggGroup(FROM_FSTRING(mesh->GetDaeId()) + "." +
|
||||
// material_semantic);
|
||||
primitiveholder = new EggGroup;
|
||||
mesh_group->add_child(primitiveholder);
|
||||
} else {
|
||||
primitiveholder = mesh_group;
|
||||
}
|
||||
primitive_holders[gr] = primitiveholder;
|
||||
// Apply the per-group data of the materials, if we have it.
|
||||
if (materials != nullptr) {
|
||||
materials->apply_to_group(material_semantic, primitiveholder, _invert_transparency);
|
||||
}
|
||||
// Find the position sources
|
||||
const FCDGeometryPolygonsInput* pinput = polygons->FindInput(FUDaeGeometryInput::POSITION);
|
||||
assert(pinput != nullptr);
|
||||
const uint32* indices = pinput->GetIndices();
|
||||
// Find the normal sources
|
||||
const FCDGeometrySource* nsource = mesh->FindSourceByType(FUDaeGeometryInput::NORMAL);
|
||||
const FCDGeometryPolygonsInput* ninput = polygons->FindInput(FUDaeGeometryInput::NORMAL);
|
||||
const uint32* nindices;
|
||||
if (ninput != nullptr) nindices = ninput->GetIndices();
|
||||
// Find texcoord sources
|
||||
const FCDGeometrySource* tcsource = mesh->FindSourceByType(FUDaeGeometryInput::TEXCOORD);
|
||||
const FCDGeometryPolygonsInput* tcinput = polygons->FindInput(FUDaeGeometryInput::TEXCOORD);
|
||||
const uint32* tcindices;
|
||||
if (tcinput != nullptr) tcindices = tcinput->GetIndices();
|
||||
// Find vcolor sources
|
||||
const FCDGeometrySource* csource = mesh->FindSourceByType(FUDaeGeometryInput::COLOR);
|
||||
const FCDGeometryPolygonsInput* cinput = polygons->FindInput(FUDaeGeometryInput::COLOR);
|
||||
const uint32* cindices;
|
||||
if (cinput != nullptr) cindices = cinput->GetIndices();
|
||||
// Find binormal sources
|
||||
const FCDGeometrySource* bsource = mesh->FindSourceByType(FUDaeGeometryInput::TEXBINORMAL);
|
||||
const FCDGeometryPolygonsInput* binput = polygons->FindInput(FUDaeGeometryInput::TEXBINORMAL);
|
||||
const uint32* bindices;
|
||||
if (binput != nullptr) bindices = binput->GetIndices();
|
||||
// Find tangent sources
|
||||
const FCDGeometrySource* tsource = mesh->FindSourceByType(FUDaeGeometryInput::TEXTANGENT);
|
||||
const FCDGeometryPolygonsInput* tinput = polygons->FindInput(FUDaeGeometryInput::TEXTANGENT);
|
||||
const uint32* tindices;
|
||||
if (tinput != nullptr) tindices = tinput->GetIndices();
|
||||
// Get a name for potential coordinate sets
|
||||
string tcsetname;
|
||||
if (materials != nullptr && tcinput != nullptr) {
|
||||
if (daeegg_cat.is_debug()) {
|
||||
daeegg_cat.debug()
|
||||
<< "Assigning texcoord set " << tcinput->GetSet()
|
||||
<< " to semantic '" << material_semantic << "'\n";
|
||||
}
|
||||
tcsetname = materials->get_uvset_name(material_semantic,
|
||||
FUDaeGeometryInput::TEXCOORD, tcinput->GetSet());
|
||||
}
|
||||
string tbsetname;
|
||||
if (materials != nullptr && binput != nullptr) {
|
||||
if (daeegg_cat.is_debug()) {
|
||||
daeegg_cat.debug()
|
||||
<< "Assigning texbinormal set " << binput->GetSet()
|
||||
<< " to semantic '" << material_semantic << "'\n";
|
||||
}
|
||||
tbsetname = materials->get_uvset_name(material_semantic,
|
||||
FUDaeGeometryInput::TEXBINORMAL, binput->GetSet());
|
||||
}
|
||||
string ttsetname;
|
||||
if (materials != nullptr && tinput != nullptr) {
|
||||
if (daeegg_cat.is_debug()) {
|
||||
daeegg_cat.debug()
|
||||
<< "Assigning textangent set " << tinput->GetSet()
|
||||
<< " to semantic '" << material_semantic << "'\n";
|
||||
}
|
||||
ttsetname = materials->get_uvset_name(material_semantic,
|
||||
FUDaeGeometryInput::TEXTANGENT, tinput->GetSet());
|
||||
}
|
||||
// Loop through the indices and add the vertices.
|
||||
for (size_t ix = 0; ix < pinput->GetIndexCount(); ++ix) {
|
||||
PT_EggVertex vertex = mesh_pool->make_new_vertex();
|
||||
const float* data = &vsource->GetData()[indices[ix]*3];
|
||||
vertex->set_pos(LPoint3d(data[0], data[1], data[2]));
|
||||
|
||||
if (character != nullptr) {
|
||||
// If this is skinned geometry, add the vertex influences.
|
||||
character->influence_vertex(indices[ix], vertex);
|
||||
}
|
||||
|
||||
// Process the normal
|
||||
if (nsource != nullptr && ninput != nullptr) {
|
||||
assert(nsource->GetStride() == 3);
|
||||
data = &nsource->GetData()[nindices[ix]*3];
|
||||
vertex->set_normal(LVecBase3d(data[0], data[1], data[2]));
|
||||
}
|
||||
// Process the texcoords
|
||||
if (tcsource != nullptr && tcinput != nullptr) {
|
||||
assert(tcsource->GetStride() == 2 || tcsource->GetStride() == 3);
|
||||
data = &tcsource->GetData()[tcindices[ix]*tcsource->GetStride()];
|
||||
if (tcsource->GetStride() == 2) {
|
||||
vertex->set_uv(tcsetname, LPoint2d(data[0], data[1]));
|
||||
} else {
|
||||
vertex->set_uvw(tcsetname, LPoint3d(data[0], data[1], data[2]));
|
||||
}
|
||||
}
|
||||
// Process the color
|
||||
if (csource != nullptr && cinput != nullptr) {
|
||||
assert(csource->GetStride() == 3 || csource->GetStride() == 4);
|
||||
if (csource->GetStride() == 3) {
|
||||
data = &csource->GetData()[cindices[ix]*3];
|
||||
vertex->set_color(LColor(data[0], data[1], data[2], 1.0f));
|
||||
} else {
|
||||
data = &csource->GetData()[cindices[ix]*4];
|
||||
vertex->set_color(LColor(data[0], data[1], data[2], data[3]));
|
||||
}
|
||||
}
|
||||
// Possibly add a UV object
|
||||
if ((bsource != nullptr && binput != nullptr) || (tsource != nullptr && tinput != nullptr)) {
|
||||
if (bsource != nullptr && binput != nullptr) {
|
||||
assert(bsource->GetStride() == 3);
|
||||
data = &bsource->GetData()[bindices[ix]*3];
|
||||
PT(EggVertexUV) uv_obj = vertex->modify_uv_obj(tbsetname);
|
||||
if (uv_obj == nullptr) {
|
||||
uv_obj = new EggVertexUV(tbsetname, LTexCoordd());
|
||||
}
|
||||
uv_obj->set_binormal(LVecBase3d(data[0], data[1], data[2]));
|
||||
}
|
||||
if (tsource != nullptr && tinput != nullptr) {
|
||||
assert(tsource->GetStride() == 3);
|
||||
data = &tsource->GetData()[tindices[ix]*3];
|
||||
PT(EggVertexUV) uv_obj = vertex->modify_uv_obj(ttsetname);
|
||||
if (uv_obj == nullptr) {
|
||||
uv_obj = new EggVertexUV(ttsetname, LTexCoordd());
|
||||
}
|
||||
uv_obj->set_tangent(LVecBase3d(data[0], data[1], data[2]));
|
||||
}
|
||||
}
|
||||
vertex->transform(parent->get_node_to_vertex());
|
||||
}
|
||||
}
|
||||
// Loop again for the polygons
|
||||
for (size_t gr = 0; gr < mesh->GetPolygonsCount(); ++gr) {
|
||||
const FCDGeometryPolygons* polygons = mesh->GetPolygons(gr);
|
||||
// Now loop through the faces
|
||||
uint32 offset = 0;
|
||||
for (size_t fa = 0; fa < polygons->GetFaceVertexCountCount(); ++fa) {
|
||||
PT(EggPrimitive) primitive = nullptr;
|
||||
// Create a primitive that matches the fcollada type
|
||||
switch (polygons->GetPrimitiveType()) {
|
||||
case FCDGeometryPolygons::LINES:
|
||||
primitive = new EggLine();
|
||||
break;
|
||||
case FCDGeometryPolygons::POLYGONS:
|
||||
primitive = new EggPolygon();
|
||||
break;
|
||||
case FCDGeometryPolygons::TRIANGLE_FANS:
|
||||
primitive = new EggTriangleFan();
|
||||
break;
|
||||
case FCDGeometryPolygons::TRIANGLE_STRIPS:
|
||||
primitive = new EggTriangleStrip();
|
||||
break;
|
||||
case FCDGeometryPolygons::POINTS:
|
||||
primitive = new EggPoint();
|
||||
break;
|
||||
case FCDGeometryPolygons::LINE_STRIPS:
|
||||
daeegg_cat.warning() << "Linestrips not yet supported!" << endl;
|
||||
break;
|
||||
default:
|
||||
daeegg_cat.warning() << "Unsupported primitive type found!" << endl;
|
||||
}
|
||||
if (primitive != nullptr) {
|
||||
primitive_holders[gr]->add_child(primitive);
|
||||
if (materials != nullptr) {
|
||||
materials->apply_to_primitive(FROM_FSTRING(polygons->GetMaterialSemantic()), primitive);
|
||||
}
|
||||
for (size_t ve = 0; ve < polygons->GetFaceVertexCount(fa); ++ve) {
|
||||
assert(mesh_pool->has_vertex(ve + polygons->GetFaceVertexOffset() + offset));
|
||||
primitive->add_vertex(mesh_pool->get_vertex(ve + polygons->GetFaceVertexOffset() + offset));
|
||||
}
|
||||
}
|
||||
offset += polygons->GetFaceVertexCount(fa);
|
||||
}
|
||||
}
|
||||
delete[] primitive_holders;
|
||||
}
|
||||
|
||||
void DAEToEggConverter::
|
||||
process_spline(EggGroup *parent, const string group_name, FCDGeometrySpline* geometry_spline) {
|
||||
assert(geometry_spline != nullptr);
|
||||
PT(EggGroup) result = new EggGroup(group_name);
|
||||
parent->add_child(result);
|
||||
// TODO: if its not a nurbs, make it convert between the types
|
||||
if (geometry_spline->GetType() != FUDaeSplineType::NURBS) {
|
||||
daeegg_cat.warning() << "Only NURBS curves are supported (yet)!" << endl;
|
||||
} else {
|
||||
// Loop through the splines
|
||||
for (size_t sp = 0; sp < geometry_spline->GetSplineCount(); ++sp) {
|
||||
process_spline(result, geometry_spline->GetSpline(sp));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DAEToEggConverter::
|
||||
process_spline(EggGroup *parent, const FCDSpline* spline) {
|
||||
assert(spline != nullptr);
|
||||
nassertv(spline->GetSplineType() == FUDaeSplineType::NURBS);
|
||||
// Now load in the nurbs curve to the egg library
|
||||
PT(EggNurbsCurve) nurbs_curve = new EggNurbsCurve(FROM_FSTRING(spline->GetName()));
|
||||
parent->add_child(nurbs_curve);
|
||||
// TODO: what value is this?
|
||||
nurbs_curve->setup(0, ((const FCDNURBSSpline*) spline)->GetKnotCount());
|
||||
for (size_t kn = 0; kn < ((const FCDNURBSSpline*) spline)->GetKnotCount(); ++kn) {
|
||||
const float* knot = ((const FCDNURBSSpline*) spline)->GetKnot(kn);
|
||||
assert(knot != nullptr);
|
||||
nurbs_curve->set_knot(kn, *knot);
|
||||
}
|
||||
for (size_t cv = 0; cv < spline->GetCVCount(); ++cv) {
|
||||
PT_EggVertex c_vtx = new EggVertex();
|
||||
c_vtx->set_pos(TO_VEC3(*spline->GetCV(cv)));
|
||||
c_vtx->transform(parent->get_node_to_vertex());
|
||||
nurbs_curve->add_vertex(c_vtx);
|
||||
}
|
||||
}
|
||||
|
||||
void DAEToEggConverter::
|
||||
process_controller(EggGroup *parent, const FCDControllerInstance *instance) {
|
||||
assert(instance != nullptr);
|
||||
const FCDController* controller = (const FCDController *)instance->GetEntity();
|
||||
assert(controller != nullptr);
|
||||
|
||||
if (get_animation_convert() == AC_none) {
|
||||
// If we're exporting a static mesh, export the base geometry as-is.
|
||||
const FCDGeometryMesh *mesh = controller->GetBaseGeometry()->GetMesh();
|
||||
if (mesh != nullptr) {
|
||||
PT(DaeMaterials) materials = new DaeMaterials(instance);
|
||||
if (daeegg_cat.is_spam()) {
|
||||
daeegg_cat.spam() << "Processing mesh for controller\n";
|
||||
}
|
||||
process_mesh(parent, mesh, materials);
|
||||
}
|
||||
} else {
|
||||
// Add a character for this to the table, the mesh is processed later
|
||||
PT(DaeCharacter) character = new DaeCharacter(parent, instance);
|
||||
_characters.push_back(character);
|
||||
}
|
||||
|
||||
if (controller->IsMorph()) {
|
||||
assert(controller != nullptr);
|
||||
const FCDMorphController* morph_controller = controller->GetMorphController();
|
||||
assert(morph_controller != nullptr);
|
||||
PT(EggTable) bundle = new EggTable(parent->get_name());
|
||||
bundle->set_table_type(EggTable::TT_bundle);
|
||||
PT(EggTable) morph = new EggTable("morph");
|
||||
morph->set_table_type(EggTable::TT_table);
|
||||
bundle->add_child(morph);
|
||||
// Loop through the morph targets.
|
||||
for (size_t mt = 0; mt < morph_controller->GetTargetCount(); ++mt) {
|
||||
const FCDMorphTarget* morph_target = morph_controller->GetTarget(mt);
|
||||
assert(morph_target != nullptr);
|
||||
PT(EggSAnimData) target = new EggSAnimData(FROM_FSTRING(morph_target->GetGeometry()->GetName()));
|
||||
if (morph_target->IsAnimated()) {
|
||||
// TODO
|
||||
} else {
|
||||
target->add_data(morph_target->GetWeight());
|
||||
}
|
||||
morph->add_child(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DAEToEggConverter::
|
||||
process_extra(EggGroup *group, const FCDExtra* extra) {
|
||||
if (extra == nullptr) {
|
||||
return;
|
||||
}
|
||||
nassertv(group != nullptr);
|
||||
|
||||
const FCDEType* etype = extra->GetDefaultType();
|
||||
if (etype == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const FCDENode* enode = (const FCDENode*) etype->FindTechnique("PANDA3D");
|
||||
if (enode == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
FCDENodeList tags;
|
||||
enode->FindChildrenNodes("param", tags);
|
||||
for (FCDENodeList::iterator it = tags.begin(); it != tags.end(); ++it) {
|
||||
const FCDEAttribute* attr = (*it)->FindAttribute("sid");
|
||||
if (attr) {
|
||||
group->set_tag(FROM_FSTRING(attr->GetValue()), (*it)->GetContent());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LMatrix4d DAEToEggConverter::
|
||||
convert_matrix(const FMMatrix44 &matrix) {
|
||||
return LMatrix4d(
|
||||
matrix[0][0], matrix[0][1], matrix[0][2], matrix[0][3],
|
||||
matrix[1][0], matrix[1][1], matrix[1][2], matrix[1][3],
|
||||
matrix[2][0], matrix[2][1], matrix[2][2], matrix[2][3],
|
||||
matrix[3][0], matrix[3][1], matrix[3][2], matrix[3][3]);
|
||||
}
|
||||
|
||||
void DAEToEggConverter::
|
||||
apply_transform(EggGroup *to, const FCDTransform* from) {
|
||||
assert(from != nullptr);
|
||||
assert(to != nullptr);
|
||||
// to->set_transform3d(convert_matrix(from->ToMatrix()) *
|
||||
// to->get_transform3d());
|
||||
switch (from->GetType()) {
|
||||
case FCDTransform::TRANSLATION:
|
||||
{
|
||||
const FCDTTranslation *trans = (const FCDTTranslation *)from;
|
||||
to->add_translate3d(TO_VEC3(trans->GetTranslation()));
|
||||
}
|
||||
break;
|
||||
|
||||
case FCDTransform::ROTATION:
|
||||
{
|
||||
const FCDTRotation *rot = (const FCDTRotation *)from;
|
||||
to->add_rotate3d(rot->GetAngle(), TO_VEC3(rot->GetAxis()));
|
||||
}
|
||||
break;
|
||||
|
||||
case FCDTransform::SCALE:
|
||||
{
|
||||
const FCDTScale *scale = (const FCDTScale *)from;
|
||||
to->add_scale3d(TO_VEC3(scale->GetScale()));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// Either a matrix, or something we can't handle.
|
||||
to->add_matrix4(convert_matrix(from->ToMatrix()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1,87 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file daeToEggConverter.h
|
||||
* @author rdb
|
||||
* @date 2008-05-08
|
||||
*/
|
||||
|
||||
#ifndef DAETOEGGCONVERTER_H
|
||||
#define DAETOEGGCONVERTER_H
|
||||
|
||||
#include "pandatoolbase.h"
|
||||
#include "somethingToEggConverter.h"
|
||||
#include "eggGroup.h"
|
||||
#include "eggMaterial.h"
|
||||
#include "eggTexture.h"
|
||||
#include "eggTable.h"
|
||||
#include "eggNurbsCurve.h"
|
||||
|
||||
#include "pre_fcollada_include.h"
|
||||
#include <FCollada.h>
|
||||
#include <FCDocument/FCDocument.h>
|
||||
#include <FCDocument/FCDTransform.h>
|
||||
#include <FCDocument/FCDEntityInstance.h>
|
||||
#include <FCDocument/FCDControllerInstance.h>
|
||||
#include <FCDocument/FCDGeometryMesh.h>
|
||||
#include <FCDocument/FCDGeometrySpline.h>
|
||||
#include <FCDocument/FCDMaterial.h>
|
||||
#include <FMath/FMMatrix44.h>
|
||||
|
||||
#include "daeMaterials.h"
|
||||
#include "daeCharacter.h"
|
||||
#include "pvector.h" // Include last
|
||||
|
||||
/**
|
||||
* This class supervises the construction of an EggData structure from a DAE
|
||||
* file.
|
||||
*/
|
||||
class DAEToEggConverter : public SomethingToEggConverter {
|
||||
public:
|
||||
DAEToEggConverter();
|
||||
DAEToEggConverter(const DAEToEggConverter ©);
|
||||
~DAEToEggConverter();
|
||||
|
||||
virtual SomethingToEggConverter *make_copy();
|
||||
|
||||
virtual std::string get_name() const;
|
||||
virtual std::string get_extension() const;
|
||||
|
||||
virtual bool convert_file(const Filename &filename);
|
||||
virtual DistanceUnit get_input_units();
|
||||
|
||||
bool _invert_transparency;
|
||||
|
||||
private:
|
||||
std::string _unit_name;
|
||||
double _unit_meters;
|
||||
PT(EggTable) _table;
|
||||
FCDocument* _document;
|
||||
FUErrorSimpleHandler* _error_handler;
|
||||
DaeCharacter::JointMap _joints;
|
||||
|
||||
typedef pvector<PT(DaeCharacter)> Characters;
|
||||
Characters _characters;
|
||||
|
||||
void process_asset();
|
||||
void process_node(EggGroupNode *parent, const FCDSceneNode* node, bool forced = false);
|
||||
void process_instance(EggGroup *parent, const FCDEntityInstance* instance);
|
||||
void process_mesh(EggGroup *parent, const FCDGeometryMesh* mesh,
|
||||
DaeMaterials *materials, DaeCharacter *character = nullptr);
|
||||
void process_spline(EggGroup *parent, const std::string group_name, FCDGeometrySpline* geometry_spline);
|
||||
void process_spline(EggGroup *parent, const FCDSpline* spline);
|
||||
void process_controller(EggGroup *parent, const FCDControllerInstance* instance);
|
||||
void process_extra(EggGroup *group, const FCDExtra* extra);
|
||||
|
||||
static LMatrix4d convert_matrix(const FMMatrix44& matrix);
|
||||
void apply_transform(EggGroup *to, const FCDTransform* from);
|
||||
|
||||
friend class DaeCharacter;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,38 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file fcollada_utils.h
|
||||
* @author rdb
|
||||
* @date 2008-12-22
|
||||
*/
|
||||
|
||||
// This file defines some conversion tools for conversion between FCollada and
|
||||
// Panda3D
|
||||
|
||||
#ifndef FCOLLADA_UTILS_H
|
||||
#define FCOLLADA_UTILS_H
|
||||
|
||||
#include "pre_fcollada_include.h"
|
||||
#include <FCollada.h>
|
||||
|
||||
// Useful conversion stuff
|
||||
inline LVecBase3d TO_VEC3(FMVector3 v) {
|
||||
return LVecBase3d(v.x, v.y, v.z);
|
||||
}
|
||||
inline LVecBase4d TO_VEC4(FMVector4 v) {
|
||||
return LVecBase4d(v.x, v.y, v.z, v.w);
|
||||
}
|
||||
inline LColor TO_COLOR(FMVector4 v) {
|
||||
return LColor(v.x, v.y, v.z, v.w);
|
||||
}
|
||||
#define FROM_VEC3(v) (FMVector3(v[0], v[1], v[2]))
|
||||
#define FROM_VEC4(v) (FMVector4(v[0], v[1], v[2], v[3]))
|
||||
#define FROM_MAT4(v) (FMMatrix44(v.getData()))
|
||||
#define FROM_FSTRING(fs) (std::string(fs.c_str()))
|
||||
|
||||
#endif
|
||||
@ -1,5 +0,0 @@
|
||||
|
||||
#include "config_daeegg.cxx"
|
||||
#include "daeCharacter.cxx"
|
||||
#include "daeMaterials.cxx"
|
||||
#include "daeToEggConverter.cxx"
|
||||
@ -1,45 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file pre_fcollada_include.h
|
||||
* @author rdb
|
||||
* @date 2008-10-04
|
||||
*/
|
||||
|
||||
// This file defines some stuff that need to be defined before one includes
|
||||
// FCollada.h
|
||||
|
||||
#ifndef PRE_FCOLLADA_INCLUDE_H
|
||||
#define PRE_FCOLLADA_INCLUDE_H
|
||||
|
||||
#ifdef FCOLLADA_VERSION
|
||||
#error You must include pre_fcollada_include.h before including FCollada.h!
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN 1
|
||||
#endif
|
||||
#include <winsock2.h>
|
||||
#endif
|
||||
|
||||
// FCollada expects LINUX to be defined on linux
|
||||
#ifdef IS_LINUX
|
||||
#ifndef LINUX
|
||||
#define LINUX
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define NO_LIBXML
|
||||
#define FCOLLADA_NOMINMAX
|
||||
|
||||
// FCollada does use global min/max.
|
||||
using std::min;
|
||||
using std::max;
|
||||
|
||||
#endif
|
||||
@ -1,11 +0,0 @@
|
||||
if(NOT BUILD_TOOLS)
|
||||
return()
|
||||
endif()
|
||||
|
||||
if(HAVE_EGG AND HAVE_FCOLLADA)
|
||||
|
||||
add_executable(dae2egg daeToEgg.cxx daeToEgg.h)
|
||||
target_link_libraries(dae2egg p3daeegg p3eggbase p3progbase)
|
||||
install(TARGETS dae2egg EXPORT Tools COMPONENT Tools DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
|
||||
endif()
|
||||
@ -1,82 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file daeToEgg.cxx
|
||||
* @author rdb
|
||||
* @date 2008-05-08
|
||||
*/
|
||||
|
||||
#include "daeToEgg.h"
|
||||
|
||||
#include "daeToEggConverter.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
DAEToEgg::
|
||||
DAEToEgg():
|
||||
SomethingToEgg("COLLADA", ".dae")
|
||||
{
|
||||
add_animation_options();
|
||||
add_units_options();
|
||||
add_normals_options();
|
||||
add_transform_options();
|
||||
|
||||
add_option
|
||||
("invtrans", "", false,
|
||||
"Import the .dae file using inverted transparency. "
|
||||
"This is useful when importing COLLADA files from some authoring tools "
|
||||
"that export models with inverted transparency, such as Google SketchUp.",
|
||||
&SomethingToEgg::dispatch_none, &_invert_transparency);
|
||||
|
||||
set_program_brief("convert COLLADA assets into .egg files");
|
||||
set_program_description
|
||||
("This program converts .dae files (COLLADA Digital Asset Exchange) to .egg.");
|
||||
|
||||
_coordinate_system = CS_yup_right;
|
||||
_animation_convert = AC_both;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void DAEToEgg::
|
||||
run() {
|
||||
if (_animation_convert != AC_both && _animation_convert != AC_none &&
|
||||
_animation_convert != AC_chan && _animation_convert != AC_model) {
|
||||
std::cerr << "Unsupported animation convert option.\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
nout << "Reading " << _input_filename << "\n";
|
||||
|
||||
_data->set_coordinate_system(_coordinate_system);
|
||||
|
||||
DAEToEggConverter converter;
|
||||
converter.set_egg_data(_data);
|
||||
converter._allow_errors = _allow_errors;
|
||||
converter._invert_transparency = _invert_transparency;
|
||||
|
||||
apply_parameters(converter);
|
||||
|
||||
if (!converter.convert_file(_input_filename)) {
|
||||
nout << "Errors in conversion.\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
write_egg_file();
|
||||
nout << "\n";
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
DAEToEgg prog;
|
||||
prog.parse_command_line(argc, argv);
|
||||
prog.run();
|
||||
return 0;
|
||||
}
|
||||
@ -1,35 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file daeToEgg.h
|
||||
* @author rdb
|
||||
* @date 2008-05-08
|
||||
*/
|
||||
|
||||
#ifndef DAETOEGG_H
|
||||
#define DAETOEGG_H
|
||||
|
||||
#include "pandatoolbase.h"
|
||||
|
||||
#include "somethingToEgg.h"
|
||||
#include "daeToEggConverter.h"
|
||||
|
||||
/**
|
||||
* A program to read a DAE file and generate an egg file.
|
||||
*/
|
||||
class DAEToEgg : public SomethingToEgg {
|
||||
public:
|
||||
DAEToEgg();
|
||||
|
||||
void run();
|
||||
|
||||
private:
|
||||
bool _invert_transparency;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,173 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file eggToDAE.cxx
|
||||
* @author rdb
|
||||
* @date 2008-10-04
|
||||
*/
|
||||
|
||||
#include "eggToDAE.h"
|
||||
#include "dcast.h"
|
||||
#include "pandaVersion.h"
|
||||
|
||||
#include <FCDocument/FCDocument.h>
|
||||
#include <FCDocument/FCDAsset.h>
|
||||
#include <FCDocument/FCDTransform.h>
|
||||
|
||||
// Useful conversion stuff
|
||||
#define TO_VEC3(v) (LVecBase3d(v[0], v[1], v[2]))
|
||||
#define TO_VEC4(v) (LVecBase4d(v[0], v[1], v[2], v[3]))
|
||||
#define TO_COLOR(v) (LColor(v[0], v[1], v[2], v[3]))
|
||||
#define FROM_VEC3(v) (FMVector3(v[0], v[1], v[2]))
|
||||
#define FROM_VEC4(v) (FMVector4(v[0], v[1], v[2], v[3]))
|
||||
#define FROM_MAT4(v) (FMMatrix44(v.get_data()))
|
||||
#define FROM_FSTRING(fs) (fs.c_str())
|
||||
|
||||
using std::cerr;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
EggToDAE::
|
||||
EggToDAE() :
|
||||
EggToSomething("COLLADA", ".dae", true, false)
|
||||
{
|
||||
set_binary_output(false);
|
||||
set_program_brief("convert .egg files into COLLADA asset files");
|
||||
set_program_description
|
||||
("This program converts files from the egg format to the COLLADA "
|
||||
".dae (Digital Asset Exchange) format.");
|
||||
|
||||
_document = nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void EggToDAE::
|
||||
run() {
|
||||
nassertv(has_output_filename());
|
||||
nassertv(_data != nullptr);
|
||||
|
||||
FCollada::Initialize();
|
||||
_document = FCollada::NewTopDocument();
|
||||
|
||||
// Add the contributor part to the asset
|
||||
FCDAssetContributor* contributor = _document->GetAsset()->AddContributor();
|
||||
const char* user_name = getenv("USER");
|
||||
if (user_name == nullptr) user_name = getenv("USERNAME");
|
||||
if (user_name != nullptr) contributor->SetAuthor(TO_FSTRING(user_name));
|
||||
// contributor->SetSourceData();
|
||||
char authoring_tool[1024];
|
||||
snprintf(authoring_tool, 1024, "Panda3D %s eggToDAE converter | FCollada v%d.%02d", PANDA_VERSION_STR, FCOLLADA_VERSION >> 16, FCOLLADA_VERSION & 0xFFFF);
|
||||
authoring_tool[1023] = 0;
|
||||
contributor->SetAuthoringTool(TO_FSTRING(authoring_tool));
|
||||
|
||||
// Set coordinate system
|
||||
switch (_data->get_coordinate_system()) {
|
||||
case CS_zup_right:
|
||||
_document->GetAsset()->SetUpAxis(FMVector3::ZAxis);
|
||||
break;
|
||||
case CS_yup_right:
|
||||
_document->GetAsset()->SetUpAxis(FMVector3::YAxis);
|
||||
break;
|
||||
}
|
||||
|
||||
// Now actually start processing the data.
|
||||
FCDSceneNode* visual_scene = _document->AddVisualScene();
|
||||
for (EggGroupNode::iterator it = _data->begin(); it != _data->end(); ++it) {
|
||||
if ((*it)->is_of_type(EggGroup::get_class_type())) {
|
||||
process_node(visual_scene, DCAST(EggGroup, *it));
|
||||
}
|
||||
}
|
||||
|
||||
// We're done here.
|
||||
FCollada::SaveDocument(_document, get_output_filename().to_os_specific().c_str());
|
||||
SAFE_DELETE(_document);
|
||||
FCollada::Release();
|
||||
|
||||
// if (!out) { nout << "An error occurred while writing.\n"; exit(1); }
|
||||
}
|
||||
|
||||
void EggToDAE::process_node(FCDSceneNode* parent, const PT(EggGroup) node) {
|
||||
assert(node != nullptr);
|
||||
FCDSceneNode* scene_node = parent->AddChildNode();
|
||||
// Set the parameters
|
||||
scene_node->SetDaeId(node->get_name().c_str());
|
||||
scene_node->SetJointFlag(node->is_joint());
|
||||
// Apply the transforms
|
||||
apply_transform(scene_node, node);
|
||||
// Recursively process sub-nodes
|
||||
for (EggGroupNode::iterator it = node->begin(); it != node->end(); ++it) {
|
||||
if ((*it)->is_of_type(EggGroup::get_class_type())) {
|
||||
process_node(scene_node, DCAST(EggGroup, *it));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EggToDAE::apply_transform(FCDSceneNode* to, const PT(EggGroup) from) {
|
||||
assert(to != nullptr);
|
||||
assert(from != nullptr);
|
||||
for (int co = 0; co < from->get_num_components(); ++co) {
|
||||
switch (from->get_component_type(co)) {
|
||||
case EggTransform::CT_translate2d:
|
||||
cerr << "Warning: ignoring non-supported 2d translation\n";
|
||||
break;
|
||||
case EggTransform::CT_rotate2d:
|
||||
cerr << "Warning: ignoring non-supported 2d rotation\n";
|
||||
break;
|
||||
case EggTransform::CT_scale2d:
|
||||
cerr << "Warning: ignoring non-supported 2d scaling\n";
|
||||
break;
|
||||
case EggTransform::CT_matrix3:
|
||||
cerr << "Warning: ignoring non-supported 2d matrix\n";
|
||||
break;
|
||||
case EggTransform::CT_translate3d: {
|
||||
FCDTTranslation* new_transform = (FCDTTranslation*) to->AddTransform(FCDTransform::TRANSLATION);
|
||||
new_transform->SetTranslation(FROM_VEC3(from->get_component_vec3(co)));
|
||||
break; }
|
||||
case EggTransform::CT_rotate3d: {
|
||||
FCDTRotation* new_transform = (FCDTRotation*) to->AddTransform(FCDTransform::ROTATION);
|
||||
new_transform->SetRotation(FROM_VEC3(from->get_component_vec3(co)), from->get_component_number(co));
|
||||
break; }
|
||||
case EggTransform::CT_scale3d: {
|
||||
FCDTScale* new_transform = (FCDTScale*) to->AddTransform(FCDTransform::SCALE);
|
||||
new_transform->SetScale(FROM_VEC3(from->get_component_vec3(co)));
|
||||
break; }
|
||||
case EggTransform::CT_matrix4: {
|
||||
FCDTMatrix* new_transform = (FCDTMatrix*) to->AddTransform(FCDTransform::MATRIX);
|
||||
new_transform->SetTransform(FROM_MAT4(from->get_component_mat4(co)));
|
||||
break; }
|
||||
case EggTransform::CT_rotx: {
|
||||
FCDTRotation* new_transform = (FCDTRotation*) to->AddTransform(FCDTransform::ROTATION);
|
||||
new_transform->SetRotation(FMVector3::XAxis, from->get_component_number(co));
|
||||
break; }
|
||||
case EggTransform::CT_roty: {
|
||||
FCDTRotation* new_transform = (FCDTRotation*) to->AddTransform(FCDTransform::ROTATION);
|
||||
new_transform->SetRotation(FMVector3::YAxis, from->get_component_number(co));
|
||||
break; }
|
||||
case EggTransform::CT_rotz: {
|
||||
FCDTRotation* new_transform = (FCDTRotation*) to->AddTransform(FCDTransform::ROTATION);
|
||||
new_transform->SetRotation(FMVector3::ZAxis, from->get_component_number(co));
|
||||
break; }
|
||||
case EggTransform::CT_uniform_scale: {
|
||||
FCDTScale* new_transform = (FCDTScale*) to->AddTransform(FCDTransform::SCALE);
|
||||
new_transform->SetScale(from->get_component_number(co), from->get_component_number(co), from->get_component_number(co));
|
||||
break; }
|
||||
default:
|
||||
cerr << "Warning: ignoring invalid transform\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
EggToDAE prog;
|
||||
prog.parse_command_line(argc, argv);
|
||||
prog.run();
|
||||
return 0;
|
||||
}
|
||||
@ -1,43 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file eggToDAE.h
|
||||
* @author rdb
|
||||
* @date 2008-10-04
|
||||
*/
|
||||
|
||||
#ifndef EGGTODAE_H
|
||||
#define EGGTODAE_H
|
||||
|
||||
#include "pandatoolbase.h"
|
||||
#include "eggToSomething.h"
|
||||
#include "eggGroup.h"
|
||||
#include "eggTransform.h"
|
||||
|
||||
#include "pre_fcollada_include.h"
|
||||
#include <FCollada.h>
|
||||
#include <FCDocument/FCDSceneNode.h>
|
||||
|
||||
/**
|
||||
* A program to read an egg file and write a DAE file.
|
||||
*/
|
||||
class EggToDAE : public EggToSomething {
|
||||
public:
|
||||
EggToDAE();
|
||||
|
||||
void run();
|
||||
|
||||
private:
|
||||
FCDocument* _document;
|
||||
|
||||
void process_node(FCDSceneNode* parent, const PT(EggGroup) node);
|
||||
void apply_transform(FCDSceneNode* to, const PT(EggGroup) from);
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,42 +0,0 @@
|
||||
if(NOT HAVE_PYTHON)
|
||||
return()
|
||||
endif()
|
||||
|
||||
add_executable(deploy-stub deploy-stub.c)
|
||||
|
||||
if(IS_OSX)
|
||||
target_link_options(deploy-stub PRIVATE -sectcreate __PANDA __panda /dev/null)
|
||||
set_target_properties(deploy-stub PROPERTIES
|
||||
INSTALL_RPATH "@executable_path"
|
||||
BUILD_WITH_INSTALL_RPATH ON)
|
||||
|
||||
elseif(WIN32)
|
||||
target_sources(deploy-stub PRIVATE frozen_dllmain.c)
|
||||
|
||||
elseif(IS_LINUX OR IS_FREEBSD)
|
||||
set_target_properties(deploy-stub PROPERTIES
|
||||
INSTALL_RPATH "$ORIGIN"
|
||||
BUILD_WITH_INSTALL_RPATH ON)
|
||||
target_link_options(deploy-stub PRIVATE -Wl,--disable-new-dtags -Wl,-z,origin -rdynamic)
|
||||
target_link_libraries(deploy-stub m)
|
||||
endif()
|
||||
|
||||
target_link_libraries(deploy-stub Python::Python)
|
||||
install(TARGETS deploy-stub)
|
||||
|
||||
if(WIN32 OR IS_OSX)
|
||||
add_executable(deploy-stubw WIN32 deploy-stub.c)
|
||||
|
||||
if(IS_OSX)
|
||||
target_link_options(deploy-stubw PRIVATE -sectcreate __PANDA __panda /dev/null)
|
||||
set_target_properties(deploy-stubw PROPERTIES
|
||||
INSTALL_RPATH "@executable_path/../Frameworks"
|
||||
BUILD_WITH_INSTALL_RPATH ON)
|
||||
target_compile_definitions(deploy-stubw PRIVATE MACOS_APP_BUNDLE=1)
|
||||
elseif(WIN32)
|
||||
target_sources(deploy-stubw PRIVATE frozen_dllmain.c)
|
||||
endif()
|
||||
|
||||
target_link_libraries(deploy-stubw Python::Python)
|
||||
install(TARGETS deploy-stubw)
|
||||
endif()
|
||||
@ -1,25 +0,0 @@
|
||||
package org.jnius;
|
||||
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* Special support for pyjnius.
|
||||
*/
|
||||
public class NativeInvocationHandler implements InvocationHandler {
|
||||
private long _ptr;
|
||||
|
||||
public NativeInvocationHandler(long ptr) {
|
||||
_ptr = ptr;
|
||||
}
|
||||
|
||||
public long getPythonObjectPointer() {
|
||||
return _ptr;
|
||||
}
|
||||
|
||||
public Object invoke(Object proxy, Method method, Object[] args) {
|
||||
return invoke0(proxy, method, args);
|
||||
}
|
||||
|
||||
native Object invoke0(Object proxy, Method method, Object[] args);
|
||||
}
|
||||
@ -1,53 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file android_log.c
|
||||
* @author rdb
|
||||
* @date 2021-12-10
|
||||
*/
|
||||
|
||||
#undef _POSIX_C_SOURCE
|
||||
#undef _XOPEN_SOURCE
|
||||
#define PY_SSIZE_T_CLEAN 1
|
||||
|
||||
#include "Python.h"
|
||||
#include <android/log.h>
|
||||
|
||||
/**
|
||||
* Writes a message to the Android log.
|
||||
*/
|
||||
static PyObject *
|
||||
_py_write(PyObject *self, PyObject *args) {
|
||||
int prio;
|
||||
char *tag;
|
||||
char *text;
|
||||
if (PyArg_ParseTuple(args, "iss", &prio, &tag, &text)) {
|
||||
__android_log_write(prio, tag, text);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static PyMethodDef python_simple_funcs[] = {
|
||||
{ "write", &_py_write, METH_VARARGS },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static struct PyModuleDef android_log_module = {
|
||||
PyModuleDef_HEAD_INIT,
|
||||
"android_log",
|
||||
NULL,
|
||||
-1,
|
||||
python_simple_funcs,
|
||||
NULL, NULL, NULL, NULL
|
||||
};
|
||||
|
||||
__attribute__((visibility("default")))
|
||||
PyObject *PyInit_android_log() {
|
||||
return PyModule_Create(&android_log_module);
|
||||
}
|
||||
@ -1,337 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file android_main.cxx
|
||||
* @author rdb
|
||||
* @date 2021-12-06
|
||||
*/
|
||||
|
||||
#include "config_android.h"
|
||||
#include "config_putil.h"
|
||||
#include "virtualFileMountAndroidAsset.h"
|
||||
#include "virtualFileSystem.h"
|
||||
#include "filename.h"
|
||||
#include "thread.h"
|
||||
#include "urlSpec.h"
|
||||
|
||||
#include "android_native_app_glue.h"
|
||||
|
||||
#include "Python.h"
|
||||
#include "structmember.h"
|
||||
|
||||
#include <sys/mman.h>
|
||||
#include <android/log.h>
|
||||
|
||||
#include <thread>
|
||||
|
||||
// Leave room for future expansion.
|
||||
#define MAX_NUM_POINTERS 24
|
||||
|
||||
// Define an exposed symbol where we store the offset to the module data.
|
||||
extern "C" {
|
||||
__attribute__((__visibility__("default"), used))
|
||||
volatile struct {
|
||||
uint64_t blob_offset;
|
||||
uint64_t blob_size;
|
||||
uint16_t version;
|
||||
uint16_t num_pointers;
|
||||
uint16_t codepage;
|
||||
uint16_t flags;
|
||||
uint64_t reserved;
|
||||
void *pointers[MAX_NUM_POINTERS];
|
||||
|
||||
// The reason we initialize it to -1 is because otherwise, smart linkers may
|
||||
// end up putting it in the .bss section for zero-initialized data.
|
||||
} blobinfo = {(uint64_t)-1};
|
||||
}
|
||||
|
||||
// Defined in android_log.c
|
||||
extern "C" PyObject *PyInit_android_log();
|
||||
|
||||
/**
|
||||
* Maps the binary blob at the given memory address to memory, and returns the
|
||||
* pointer to the beginning of it.
|
||||
*/
|
||||
static void *map_blob(const char *path, off_t offset, size_t size) {
|
||||
FILE *runtime = fopen(path, "rb");
|
||||
assert(runtime != NULL);
|
||||
|
||||
void *blob = (void *)mmap(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fileno(runtime), offset);
|
||||
assert(blob != MAP_FAILED);
|
||||
|
||||
fclose(runtime);
|
||||
return blob;
|
||||
}
|
||||
|
||||
/**
|
||||
* The inverse of map_blob.
|
||||
*/
|
||||
static void unmap_blob(void *blob) {
|
||||
if (blob) {
|
||||
munmap(blob, blobinfo.blob_size);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is called by native_app_glue to initialize the program.
|
||||
*
|
||||
* Note that this does not run in the main thread, but in a thread created
|
||||
* specifically for this activity by android_native_app_glue.
|
||||
*
|
||||
* Unlike the regular deploy-stub, we need to interface directly with the
|
||||
* Panda3D libraries here, since we can't pass the pointers from Java to Panda
|
||||
* through the Python interpreter easily.
|
||||
*/
|
||||
void android_main(struct android_app *app) {
|
||||
panda_android_app = app;
|
||||
|
||||
// Attach the app thread to the Java VM.
|
||||
JNIEnv *env;
|
||||
ANativeActivity *activity = app->activity;
|
||||
int attach_status = activity->vm->AttachCurrentThread(&env, nullptr);
|
||||
if (attach_status < 0 || env == nullptr) {
|
||||
android_cat.error() << "Failed to attach thread to JVM!\n";
|
||||
return;
|
||||
}
|
||||
|
||||
jclass activity_class = env->GetObjectClass(activity->clazz);
|
||||
|
||||
// Get the current Java thread name. This just helps with debugging.
|
||||
jmethodID methodID = env->GetStaticMethodID(activity_class, "getCurrentThreadName", "()Ljava/lang/String;");
|
||||
jstring jthread_name = (jstring) env->CallStaticObjectMethod(activity_class, methodID);
|
||||
|
||||
std::string thread_name;
|
||||
if (jthread_name != nullptr) {
|
||||
const char *c_str = env->GetStringUTFChars(jthread_name, nullptr);
|
||||
thread_name.assign(c_str);
|
||||
env->ReleaseStringUTFChars(jthread_name, c_str);
|
||||
}
|
||||
|
||||
// Before we make any Panda calls, we must make the thread known to Panda.
|
||||
// This will also cause the JNIEnv pointer to be stored on the thread.
|
||||
// Note that we must keep a reference to this thread around.
|
||||
PT(Thread) current_thread = Thread::bind_thread(thread_name, "android_app");
|
||||
|
||||
android_cat.info()
|
||||
<< "New native activity started on " << *current_thread << "\n";
|
||||
|
||||
// Fetch the data directory.
|
||||
jmethodID get_appinfo = env->GetMethodID(activity_class, "getApplicationInfo", "()Landroid/content/pm/ApplicationInfo;");
|
||||
|
||||
jobject appinfo = env->CallObjectMethod(activity->clazz, get_appinfo);
|
||||
jclass appinfo_class = env->GetObjectClass(appinfo);
|
||||
|
||||
// Fetch the path to the data directory.
|
||||
jfieldID datadir_field = env->GetFieldID(appinfo_class, "dataDir", "Ljava/lang/String;");
|
||||
jstring datadir = (jstring) env->GetObjectField(appinfo, datadir_field);
|
||||
const char *data_path = env->GetStringUTFChars(datadir, nullptr);
|
||||
|
||||
if (data_path != nullptr) {
|
||||
Filename::_internal_data_dir = data_path;
|
||||
android_cat.info() << "Path to data: " << data_path << "\n";
|
||||
|
||||
env->ReleaseStringUTFChars(datadir, data_path);
|
||||
}
|
||||
|
||||
// Get the cache directory. Set the model-path to this location.
|
||||
methodID = env->GetMethodID(activity_class, "getCacheDirString", "()Ljava/lang/String;");
|
||||
jstring jcache_dir = (jstring) env->CallObjectMethod(activity->clazz, methodID);
|
||||
|
||||
if (jcache_dir != nullptr) {
|
||||
const char *cache_dir;
|
||||
cache_dir = env->GetStringUTFChars(jcache_dir, nullptr);
|
||||
android_cat.info() << "Path to cache: " << cache_dir << "\n";
|
||||
|
||||
ConfigVariableFilename model_cache_dir("model-cache-dir", Filename());
|
||||
model_cache_dir.set_value(cache_dir);
|
||||
env->ReleaseStringUTFChars(jcache_dir, cache_dir);
|
||||
}
|
||||
|
||||
// Fetch the path to the library directory.
|
||||
jfieldID libdir_field = env->GetFieldID(appinfo_class, "nativeLibraryDir", "Ljava/lang/String;");
|
||||
jstring libdir_jstr = (jstring) env->GetObjectField(appinfo, libdir_field);
|
||||
const char *libdir = env->GetStringUTFChars(libdir_jstr, nullptr);
|
||||
|
||||
if (libdir != nullptr) {
|
||||
std::string dtool_name = std::string(libdir) + "/libp3dtool.so";
|
||||
ExecutionEnvironment::set_dtool_name(dtool_name);
|
||||
android_cat.info() << "Path to dtool: " << dtool_name << "\n";
|
||||
}
|
||||
|
||||
// Get the path to the APK.
|
||||
methodID = env->GetMethodID(activity_class, "getPackageCodePath", "()Ljava/lang/String;");
|
||||
jstring code_path = (jstring) env->CallObjectMethod(activity->clazz, methodID);
|
||||
|
||||
const char *apk_path;
|
||||
apk_path = env->GetStringUTFChars(code_path, nullptr);
|
||||
android_cat.info() << "Path to APK: " << apk_path << "\n";
|
||||
|
||||
// Get the path to the native library.
|
||||
methodID = env->GetMethodID(activity_class, "getNativeLibraryPath", "()Ljava/lang/String;");
|
||||
jstring lib_path_jstr = (jstring) env->CallObjectMethod(activity->clazz, methodID);
|
||||
|
||||
const char *lib_path;
|
||||
lib_path = env->GetStringUTFChars(lib_path_jstr, nullptr);
|
||||
android_cat.info() << "Path to native library: " << lib_path << "\n";
|
||||
ExecutionEnvironment::set_binary_name(lib_path);
|
||||
|
||||
// Map the blob to memory
|
||||
void *blob = map_blob(lib_path, (off_t)blobinfo.blob_offset, (size_t)blobinfo.blob_size);
|
||||
env->ReleaseStringUTFChars(lib_path_jstr, lib_path);
|
||||
assert(blob != NULL);
|
||||
|
||||
assert(blobinfo.num_pointers <= MAX_NUM_POINTERS);
|
||||
for (uint32_t i = 0; i < blobinfo.num_pointers; ++i) {
|
||||
// Only offset if the pointer is non-NULL. Except for the first
|
||||
// pointer, which may never be NULL and usually (but not always)
|
||||
// points to the beginning of the blob.
|
||||
if (i == 0 || blobinfo.pointers[i] != nullptr) {
|
||||
blobinfo.pointers[i] = (void *)((uintptr_t)blobinfo.pointers[i] + (uintptr_t)blob);
|
||||
}
|
||||
}
|
||||
|
||||
// Now load the configuration files.
|
||||
ConfigPage *page = nullptr;
|
||||
ConfigPageManager *cp_mgr;
|
||||
const char *prc_data = (char *)blobinfo.pointers[1];
|
||||
if (prc_data != nullptr) {
|
||||
cp_mgr = ConfigPageManager::get_global_ptr();
|
||||
std::istringstream in(prc_data);
|
||||
page = cp_mgr->make_explicit_page("builtin");
|
||||
page->read_prc(in);
|
||||
}
|
||||
|
||||
// Mount the assets directory.
|
||||
Filename apk_fn(apk_path);
|
||||
PT(VirtualFileMountAndroidAsset) asset_mount;
|
||||
asset_mount = new VirtualFileMountAndroidAsset(app->activity->assetManager, apk_fn);
|
||||
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
|
||||
|
||||
//Filename asset_dir(apk_fn.get_dirname(), "assets");
|
||||
Filename asset_dir("/android_asset");
|
||||
vfs->mount(asset_mount, asset_dir, 0);
|
||||
|
||||
// Release the apk_path.
|
||||
env->ReleaseStringUTFChars(code_path, apk_path);
|
||||
|
||||
// Now add the asset directory to the model-path.
|
||||
//TODO: prevent it from adding the directory multiple times.
|
||||
get_model_path().append_directory(asset_dir);
|
||||
|
||||
// Offset the pointers in the module table using the base mmap address.
|
||||
struct _frozen *moddef = (struct _frozen *)blobinfo.pointers[0];
|
||||
while (moddef->name) {
|
||||
moddef->name = (char *)((uintptr_t)moddef->name + (uintptr_t)blob);
|
||||
if (moddef->code != nullptr) {
|
||||
moddef->code = (unsigned char *)((uintptr_t)moddef->code + (uintptr_t)blob);
|
||||
}
|
||||
//__android_log_print(ANDROID_LOG_DEBUG, "Panda3D", "MOD: %s %p %d\n", moddef->name, (void*)moddef->code, moddef->size);
|
||||
moddef++;
|
||||
}
|
||||
|
||||
PyImport_FrozenModules = (struct _frozen *)blobinfo.pointers[0];
|
||||
|
||||
PyPreConfig preconfig;
|
||||
PyPreConfig_InitIsolatedConfig(&preconfig);
|
||||
preconfig.utf8_mode = 1;
|
||||
PyStatus status = Py_PreInitialize(&preconfig);
|
||||
if (PyStatus_Exception(status)) {
|
||||
env->ReleaseStringUTFChars(libdir_jstr, libdir);
|
||||
Py_ExitStatusException(status);
|
||||
return;
|
||||
}
|
||||
|
||||
// Register the android_log module.
|
||||
if (PyImport_AppendInittab("android_log", &PyInit_android_log) < 0) {
|
||||
android_cat.error()
|
||||
<< "Failed to register android_log module.\n";
|
||||
env->ReleaseStringUTFChars(libdir_jstr, libdir);
|
||||
return;
|
||||
}
|
||||
|
||||
PyConfig config;
|
||||
PyConfig_InitIsolatedConfig(&config);
|
||||
config.pathconfig_warnings = 0; /* Suppress errors from getpath.c */
|
||||
config.buffered_stdio = 0;
|
||||
config.configure_c_stdio = 0;
|
||||
config.write_bytecode = 0;
|
||||
PyConfig_SetBytesString(&config, &config.platlibdir, libdir);
|
||||
env->ReleaseStringUTFChars(libdir_jstr, libdir);
|
||||
|
||||
status = Py_InitializeFromConfig(&config);
|
||||
PyConfig_Clear(&config);
|
||||
if (PyStatus_Exception(status)) {
|
||||
Py_ExitStatusException(status);
|
||||
return;
|
||||
}
|
||||
|
||||
while (!app->destroyRequested) {
|
||||
// Call the main module. This will not return until the app is done.
|
||||
android_cat.info() << "Importing __main__\n";
|
||||
|
||||
int n = PyImport_ImportFrozenModule("__main__");
|
||||
if (n == 0) {
|
||||
Py_FatalError("__main__ not frozen");
|
||||
break;
|
||||
}
|
||||
if (n < 0) {
|
||||
if (!PyErr_ExceptionMatches(PyExc_SystemExit)) {
|
||||
PyErr_Print();
|
||||
} else {
|
||||
PyErr_Clear();
|
||||
}
|
||||
}
|
||||
|
||||
if (app->destroyRequested) {
|
||||
// The app closed responding to a destroy request.
|
||||
break;
|
||||
}
|
||||
|
||||
// Ask Android to clean up the activity.
|
||||
android_cat.info() << "Exited from __main__, finishing activity\n";
|
||||
ANativeActivity_finish(activity);
|
||||
|
||||
// We still need to keep an event loop going until Android gives us leave
|
||||
// to end the process.
|
||||
while (!app->destroyRequested) {
|
||||
int looper_id;
|
||||
struct android_poll_source *source;
|
||||
auto result = ALooper_pollOnce(-1, &looper_id, nullptr, (void **)&source);
|
||||
if (looper_id == LOOPER_ID_MAIN) {
|
||||
int8_t cmd = android_app_read_cmd(app);
|
||||
android_app_pre_exec_cmd(app, cmd);
|
||||
android_app_post_exec_cmd(app, cmd);
|
||||
|
||||
// I don't think we can get a resume command after we call finish(),
|
||||
// but let's handle it just in case.
|
||||
if (cmd == APP_CMD_RESUME || cmd == APP_CMD_DESTROY) {
|
||||
break;
|
||||
}
|
||||
} else if (source != nullptr) {
|
||||
source->process(app, source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Py_Finalize();
|
||||
|
||||
android_cat.info() << "Destroy requested, exiting from android_main\n";
|
||||
|
||||
vfs->unmount(asset_mount);
|
||||
|
||||
if (page != nullptr) {
|
||||
cp_mgr->delete_explicit_page(page);
|
||||
}
|
||||
|
||||
unmap_blob(blob);
|
||||
|
||||
// Detach the thread before exiting.
|
||||
activity->vm->DetachCurrentThread();
|
||||
|
||||
_exit(0);
|
||||
}
|
||||
@ -1,767 +0,0 @@
|
||||
/* Python interpreter main program for frozen scripts */
|
||||
|
||||
#include "Python.h"
|
||||
#ifdef _WIN32
|
||||
# include "malloc.h"
|
||||
# include <Shlobj.h>
|
||||
#else
|
||||
# include <sys/mman.h>
|
||||
# include <pwd.h>
|
||||
#endif
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
# include <sys/sysctl.h>
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
# include <mach-o/dyld.h>
|
||||
# include <libgen.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <locale.h>
|
||||
|
||||
#include "structmember.h"
|
||||
|
||||
/* Leave room for future expansion. We only read pointer 0, but there are
|
||||
other pointers that are being read by configPageManager.cxx. */
|
||||
#define MAX_NUM_POINTERS 24
|
||||
|
||||
/* Stored in the flags field of the blobinfo structure below. */
|
||||
enum Flags {
|
||||
F_log_append = 1,
|
||||
F_log_filename_strftime = 2,
|
||||
F_keep_docstrings = 4,
|
||||
};
|
||||
|
||||
/* Define an exposed symbol where we store the offset to the module data. */
|
||||
#ifdef _MSC_VER
|
||||
__declspec(dllexport)
|
||||
#else
|
||||
__attribute__((__visibility__("default"), used))
|
||||
#endif
|
||||
volatile struct {
|
||||
uint64_t blob_offset;
|
||||
uint64_t blob_size;
|
||||
uint16_t version;
|
||||
uint16_t num_pointers;
|
||||
uint16_t codepage;
|
||||
uint16_t flags;
|
||||
uint64_t reserved;
|
||||
void *pointers[MAX_NUM_POINTERS];
|
||||
|
||||
// The reason we initialize it to -1 is because otherwise, smart linkers may
|
||||
// end up putting it in the .bss section for zero-initialized data.
|
||||
} blobinfo = {(uint64_t)-1};
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
// These placeholders can have their names changed by deploy-stub.
|
||||
__declspec(dllexport) DWORD SymbolPlaceholder___________________ = 0x00000001;
|
||||
__declspec(dllexport) DWORD SymbolPlaceholder__ = 0x00000001;
|
||||
#endif
|
||||
|
||||
#ifdef MS_WINDOWS
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# include <windows.h>
|
||||
|
||||
extern void PyWinFreeze_ExeInit(void);
|
||||
extern void PyWinFreeze_ExeTerm(void);
|
||||
|
||||
static struct _inittab extensions[] = {
|
||||
{0, 0},
|
||||
};
|
||||
|
||||
# define WIN_UNICODE
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
static wchar_t *log_pathw = NULL;
|
||||
#endif
|
||||
|
||||
#if PY_VERSION_HEX >= 0x030b0000
|
||||
typedef struct {
|
||||
const char *name;
|
||||
const unsigned char *code;
|
||||
int size;
|
||||
} ModuleDef;
|
||||
#else
|
||||
typedef struct _frozen ModuleDef;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Sets the main_dir field of the blobinfo structure, but only if it wasn't
|
||||
* already set.
|
||||
*/
|
||||
static void set_main_dir(char *main_dir) {
|
||||
if (blobinfo.num_pointers >= 10) {
|
||||
if (blobinfo.num_pointers == 10) {
|
||||
++blobinfo.num_pointers;
|
||||
blobinfo.pointers[10] = NULL;
|
||||
}
|
||||
if (blobinfo.pointers[10] == NULL) {
|
||||
blobinfo.pointers[10] = main_dir;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the parent directories of the given path. Returns 1 on success.
|
||||
*/
|
||||
#ifdef _WIN32
|
||||
static int mkdir_parent(const wchar_t *path) {
|
||||
// Copy the path to a temporary buffer.
|
||||
wchar_t buffer[4096];
|
||||
size_t buflen = wcslen(path);
|
||||
if (buflen + 1 >= _countof(buffer)) {
|
||||
return 0;
|
||||
}
|
||||
wcscpy_s(buffer, _countof(buffer), path);
|
||||
|
||||
// Seek back to find the last path separator.
|
||||
while (buflen-- > 0) {
|
||||
if (buffer[buflen] == '/' || buffer[buflen] == '\\') {
|
||||
buffer[buflen] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (buflen == (size_t)-1 || buflen == 0) {
|
||||
// There was no path separator, or this was the root directory.
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (CreateDirectoryW(buffer, NULL) != 0) {
|
||||
// Success!
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Failed.
|
||||
DWORD last_error = GetLastError();
|
||||
if (last_error == ERROR_ALREADY_EXISTS) {
|
||||
// Not really an error: the directory is already there.
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (last_error == ERROR_PATH_NOT_FOUND) {
|
||||
// We need to make the parent directory first.
|
||||
if (mkdir_parent(buffer)) {
|
||||
// Parent successfully created. Try again to make the child.
|
||||
if (CreateDirectoryW(buffer, NULL) != 0) {
|
||||
// Got it!
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static int mkdir_parent(const char *path) {
|
||||
// Copy the path to a temporary buffer.
|
||||
char buffer[4096];
|
||||
size_t buflen = strlen(path);
|
||||
if (buflen + 1 >= sizeof(buffer)) {
|
||||
return 0;
|
||||
}
|
||||
strcpy(buffer, path);
|
||||
|
||||
// Seek back to find the last path separator.
|
||||
while (buflen-- > 0) {
|
||||
if (buffer[buflen] == '/') {
|
||||
buffer[buflen] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (buflen == (size_t)-1 || buflen == 0) {
|
||||
// There was no path separator, or this was the root directory.
|
||||
return 0;
|
||||
}
|
||||
if (mkdir(buffer, 0755) == 0) {
|
||||
// Success!
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Failed.
|
||||
if (errno == EEXIST) {
|
||||
// Not really an error: the directory is already there.
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (errno == ENOENT || errno == EACCES) {
|
||||
// We need to make the parent directory first.
|
||||
if (mkdir_parent(buffer)) {
|
||||
// Parent successfully created. Try again to make the child.
|
||||
if (mkdir(buffer, 0755) == 0) {
|
||||
// Got it!
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Redirects the output streams to point to the log file with the given path.
|
||||
*
|
||||
* @param path specifies the location of log file, may start with ~
|
||||
* @param append should be nonzero if it should not truncate the log file.
|
||||
*/
|
||||
static int setup_logging(const char *path, int append) {
|
||||
#ifdef _WIN32
|
||||
// Does it start with a tilde? Perform tilde expansion if so.
|
||||
wchar_t *pathw = (wchar_t *)malloc(sizeof(wchar_t) * MAX_PATH);
|
||||
pathw[0] = 0;
|
||||
size_t offset = 0;
|
||||
if (path[0] == '~' && (path[1] == 0 || path[1] == '/' || path[1] == '\\')) {
|
||||
// Strip off the tilde.
|
||||
++path;
|
||||
|
||||
// Get the home directory path for the current user.
|
||||
if (!SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_PROFILE, NULL, 0, pathw))) {
|
||||
free(pathw);
|
||||
return 0;
|
||||
}
|
||||
offset = wcslen(pathw);
|
||||
}
|
||||
|
||||
// We need to convert the rest of the path from UTF-8 to UTF-16.
|
||||
if (MultiByteToWideChar(CP_UTF8, 0, path, -1, pathw + offset,
|
||||
(int)(MAX_PATH - offset)) == 0) {
|
||||
free(pathw);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DWORD access = append ? FILE_APPEND_DATA : (GENERIC_READ | GENERIC_WRITE);
|
||||
int creation = append ? OPEN_ALWAYS : CREATE_ALWAYS;
|
||||
HANDLE handle = CreateFileW(pathw, access, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
NULL, creation, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
|
||||
if (handle == INVALID_HANDLE_VALUE) {
|
||||
// Make the parent directories first.
|
||||
mkdir_parent(pathw);
|
||||
handle = CreateFileW(pathw, access, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
NULL, creation, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
}
|
||||
|
||||
if (handle == INVALID_HANDLE_VALUE) {
|
||||
free(pathw);
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_pathw = pathw;
|
||||
|
||||
if (append) {
|
||||
SetFilePointer(handle, 0, NULL, FILE_END);
|
||||
}
|
||||
|
||||
SetStdHandle(STD_OUTPUT_HANDLE, handle);
|
||||
SetStdHandle(STD_ERROR_HANDLE, handle);
|
||||
|
||||
// If we are running under the UCRT in a GUI application, we can't be sure
|
||||
// that we have valid fds for stdout and stderr, so we have to set them up.
|
||||
// One way to do this is to reopen them to something silly (like NUL).
|
||||
if (_fileno(stdout) < 0) {
|
||||
_close(1);
|
||||
_wfreopen(L"\\\\.\\NUL", L"w", stdout);
|
||||
}
|
||||
|
||||
if (_fileno(stderr) < 0) {
|
||||
_close(2);
|
||||
_wfreopen(L"\\\\.\\NUL", L"w", stderr);
|
||||
}
|
||||
|
||||
// Now replace the stdout and stderr file descriptors with one pointing to
|
||||
// our desired handle.
|
||||
int fd = _open_osfhandle((intptr_t)handle, _O_WRONLY | _O_TEXT | _O_APPEND);
|
||||
_dup2(fd, _fileno(stdout));
|
||||
_dup2(fd, _fileno(stderr));
|
||||
_close(fd);
|
||||
|
||||
return 1;
|
||||
#else
|
||||
// Does it start with a tilde? Perform tilde expansion if so.
|
||||
char buffer[PATH_MAX * 2];
|
||||
size_t offset = 0;
|
||||
if (path[0] == '~' && (path[1] == 0 || path[1] == '/')) {
|
||||
// Strip off the tilde.
|
||||
++path;
|
||||
|
||||
// Get the home directory path for the current user.
|
||||
const char *home_dir = getenv("HOME");
|
||||
if (home_dir == NULL) {
|
||||
home_dir = getpwuid(getuid())->pw_dir;
|
||||
}
|
||||
offset = strlen(home_dir);
|
||||
assert(offset < sizeof(buffer));
|
||||
strncpy(buffer, home_dir, sizeof(buffer));
|
||||
}
|
||||
|
||||
// Copy over the rest of the path.
|
||||
strcpy(buffer + offset, path);
|
||||
|
||||
mode_t mode = O_CREAT | O_WRONLY | (append ? O_APPEND : O_TRUNC);
|
||||
int fd = open(buffer, mode, 0644);
|
||||
if (fd == -1) {
|
||||
// Make the parent directories first.
|
||||
mkdir_parent(buffer);
|
||||
fd = open(buffer, mode, 0644);
|
||||
}
|
||||
|
||||
if (fd == -1) {
|
||||
perror(buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
|
||||
dup2(fd, 1);
|
||||
dup2(fd, 2);
|
||||
|
||||
if (close(fd) < 0) {
|
||||
perror("setup_logging: close");
|
||||
}
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the line_buffering property on a TextIOWrapper object.
|
||||
*/
|
||||
static int enable_line_buffering(PyObject *file) {
|
||||
#if PY_VERSION_HEX >= 0x03070000
|
||||
/* Python 3.7 has a useful reconfigure() method. */
|
||||
PyObject *kwargs = _PyDict_NewPresized(1);
|
||||
PyDict_SetItemString(kwargs, "line_buffering", Py_True);
|
||||
PyObject *args = PyTuple_New(0);
|
||||
|
||||
PyObject *method = PyObject_GetAttrString(file, "reconfigure");
|
||||
if (method != NULL) {
|
||||
PyObject *result = PyObject_Call(method, args, kwargs);
|
||||
Py_DECREF(method);
|
||||
Py_DECREF(kwargs);
|
||||
Py_DECREF(args);
|
||||
if (result != NULL) {
|
||||
Py_DECREF(result);
|
||||
} else {
|
||||
PyErr_Clear();
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
Py_DECREF(kwargs);
|
||||
Py_DECREF(args);
|
||||
PyErr_Clear();
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
/* Older versions just don't expose a way to reconfigure(), but it's still
|
||||
safe to override the property; we just have to use a hack to do it,
|
||||
because it's officially marked "readonly". */
|
||||
|
||||
PyTypeObject *type = Py_TYPE(file);
|
||||
PyMemberDef *member = type->tp_members;
|
||||
|
||||
while (member != NULL && member->name != NULL) {
|
||||
if (strcmp(member->name, "line_buffering") == 0) {
|
||||
*((char *)file + member->offset) = 1;
|
||||
return 1;
|
||||
}
|
||||
++member;
|
||||
}
|
||||
fflush(stdout);
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Main program */
|
||||
|
||||
#ifdef WIN_UNICODE
|
||||
int Py_FrozenMain(int argc, wchar_t **argv)
|
||||
#else
|
||||
int Py_FrozenMain(int argc, char **argv)
|
||||
#endif
|
||||
{
|
||||
char *p;
|
||||
int n, sts = 1;
|
||||
int unbuffered = 0;
|
||||
#ifndef NDEBUG
|
||||
int inspect = 0;
|
||||
#endif
|
||||
|
||||
#ifndef WIN_UNICODE
|
||||
int i;
|
||||
char *oldloc;
|
||||
wchar_t **argv_copy = NULL;
|
||||
/* We need a second copies, as Python might modify the first one. */
|
||||
wchar_t **argv_copy2 = NULL;
|
||||
|
||||
if (argc > 0) {
|
||||
argv_copy = (wchar_t **)alloca(sizeof(wchar_t *) * argc);
|
||||
argv_copy2 = (wchar_t **)alloca(sizeof(wchar_t *) * argc);
|
||||
}
|
||||
#endif
|
||||
|
||||
Py_FrozenFlag = 1; /* Suppress errors from getpath.c */
|
||||
Py_NoSiteFlag = 0;
|
||||
Py_NoUserSiteDirectory = 1;
|
||||
|
||||
#if PY_VERSION_HEX >= 0x03020000
|
||||
if (blobinfo.flags & F_keep_docstrings) {
|
||||
Py_OptimizeFlag = 1;
|
||||
} else {
|
||||
Py_OptimizeFlag = 2;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef NDEBUG
|
||||
if ((p = Py_GETENV("PYTHONINSPECT")) && *p != '\0')
|
||||
inspect = 1;
|
||||
#endif
|
||||
if ((p = Py_GETENV("PYTHONUNBUFFERED")) && *p != '\0')
|
||||
unbuffered = 1;
|
||||
|
||||
if (unbuffered) {
|
||||
setbuf(stdin, (char *)NULL);
|
||||
setbuf(stdout, (char *)NULL);
|
||||
setbuf(stderr, (char *)NULL);
|
||||
}
|
||||
|
||||
#ifndef WIN_UNICODE
|
||||
oldloc = setlocale(LC_ALL, NULL);
|
||||
setlocale(LC_ALL, "");
|
||||
for (i = 0; i < argc; i++) {
|
||||
argv_copy[i] = Py_DecodeLocale(argv[i], NULL);
|
||||
argv_copy2[i] = argv_copy[i];
|
||||
if (!argv_copy[i]) {
|
||||
fprintf(stderr, "Unable to decode the command line argument #%i\n",
|
||||
i + 1);
|
||||
argc = i;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
setlocale(LC_ALL, oldloc);
|
||||
#endif
|
||||
|
||||
#ifdef MS_WINDOWS
|
||||
PyImport_ExtendInittab(extensions);
|
||||
#endif /* MS_WINDOWS */
|
||||
|
||||
if (argc >= 1) {
|
||||
#ifndef WIN_UNICODE
|
||||
Py_SetProgramName(argv_copy[0]);
|
||||
#else
|
||||
Py_SetProgramName(argv[0]);
|
||||
#endif
|
||||
}
|
||||
|
||||
Py_Initialize();
|
||||
#ifdef MS_WINDOWS
|
||||
PyWinFreeze_ExeInit();
|
||||
#endif
|
||||
|
||||
#ifdef MS_WINDOWS
|
||||
/* Ensure that line buffering is enabled on the output streams. */
|
||||
if (!unbuffered) {
|
||||
PyObject *sys_stream;
|
||||
sys_stream = PySys_GetObject("__stdout__");
|
||||
if (sys_stream && !enable_line_buffering(sys_stream)) {
|
||||
fprintf(stderr, "Failed to enable line buffering on sys.stdout\n");
|
||||
fflush(stderr);
|
||||
}
|
||||
sys_stream = PySys_GetObject("__stderr__");
|
||||
if (sys_stream && !enable_line_buffering(sys_stream)) {
|
||||
fprintf(stderr, "Failed to enable line buffering on sys.stderr\n");
|
||||
fflush(stderr);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (Py_VerboseFlag)
|
||||
fprintf(stderr, "Python %s\n%s\n",
|
||||
Py_GetVersion(), Py_GetCopyright());
|
||||
|
||||
#ifndef WIN_UNICODE
|
||||
PySys_SetArgv(argc, argv_copy);
|
||||
#else
|
||||
PySys_SetArgv(argc, argv);
|
||||
#endif
|
||||
|
||||
#ifdef MACOS_APP_BUNDLE
|
||||
// Add the Frameworks directory to sys.path.
|
||||
char buffer[PATH_MAX];
|
||||
uint32_t bufsize = sizeof(buffer);
|
||||
if (_NSGetExecutablePath(buffer, &bufsize) != 0) {
|
||||
assert(false);
|
||||
return 1;
|
||||
}
|
||||
char resolved[PATH_MAX];
|
||||
if (!realpath(buffer, resolved)) {
|
||||
perror("realpath");
|
||||
return 1;
|
||||
}
|
||||
const char *dir = dirname(resolved);
|
||||
sprintf(buffer, "%s/../Frameworks", dir);
|
||||
|
||||
PyObject *sys_path = PyList_New(1);
|
||||
PyList_SET_ITEM(sys_path, 0, PyUnicode_FromString(buffer));
|
||||
PySys_SetObject("path", sys_path);
|
||||
Py_DECREF(sys_path);
|
||||
|
||||
// Now, store a path to the Resources directory into the main_dir pointer,
|
||||
// for ConfigPageManager to read out and assign to MAIN_DIR.
|
||||
sprintf(buffer, "%s/../Resources", dir);
|
||||
set_main_dir(buffer);
|
||||
|
||||
// Finally, chdir to it, so that regular Python files are read from the
|
||||
// right location.
|
||||
chdir(buffer);
|
||||
#endif
|
||||
|
||||
n = PyImport_ImportFrozenModule("__main__");
|
||||
if (n == 0)
|
||||
Py_FatalError("__main__ not frozen");
|
||||
if (n < 0) {
|
||||
PyErr_Print();
|
||||
sts = 1;
|
||||
}
|
||||
else
|
||||
sts = 0;
|
||||
|
||||
#ifndef NDEBUG
|
||||
if (inspect && isatty((int)fileno(stdin)))
|
||||
sts = PyRun_AnyFile(stdin, "<stdin>") != 0;
|
||||
#endif
|
||||
|
||||
#ifdef MS_WINDOWS
|
||||
PyWinFreeze_ExeTerm();
|
||||
#endif
|
||||
Py_Finalize();
|
||||
|
||||
#ifndef WIN_UNICODE
|
||||
error:
|
||||
if (argv_copy2) {
|
||||
for (i = 0; i < argc; i++) {
|
||||
PyMem_RawFree(argv_copy2[i]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return sts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps the binary blob at the given memory address to memory, and returns the
|
||||
* pointer to the beginning of it.
|
||||
*/
|
||||
static void *map_blob(off_t offset, size_t size) {
|
||||
void *blob;
|
||||
FILE *runtime;
|
||||
|
||||
#ifdef _WIN32
|
||||
wchar_t buffer[2048];
|
||||
GetModuleFileNameW(NULL, buffer, 2048);
|
||||
runtime = _wfopen(buffer, L"rb");
|
||||
#elif defined(__FreeBSD__)
|
||||
size_t bufsize = 4096;
|
||||
char buffer[4096];
|
||||
int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
|
||||
mib[3] = getpid();
|
||||
if (sysctl(mib, 4, (void *)buffer, &bufsize, NULL, 0) == -1) {
|
||||
perror("sysctl");
|
||||
return NULL;
|
||||
}
|
||||
runtime = fopen(buffer, "rb");
|
||||
#elif defined(__APPLE__)
|
||||
char buffer[4096];
|
||||
uint32_t bufsize = sizeof(buffer);
|
||||
if (_NSGetExecutablePath(buffer, &bufsize) != 0) {
|
||||
return NULL;
|
||||
}
|
||||
runtime = fopen(buffer, "rb");
|
||||
#else
|
||||
char buffer[4096];
|
||||
ssize_t pathlen = readlink("/proc/self/exe", buffer, sizeof(buffer) - 1);
|
||||
if (pathlen <= 0) {
|
||||
perror("readlink(/proc/self/exe)");
|
||||
return NULL;
|
||||
}
|
||||
buffer[pathlen] = '\0';
|
||||
runtime = fopen(buffer, "rb");
|
||||
#endif
|
||||
|
||||
// Get offsets. In version 0, we read it from the end of the file.
|
||||
if (blobinfo.version == 0) {
|
||||
uint64_t end, begin;
|
||||
fseek(runtime, -8, SEEK_END);
|
||||
end = ftell(runtime);
|
||||
fread(&begin, 8, 1, runtime);
|
||||
|
||||
offset = (off_t)begin;
|
||||
size = (size_t)(end - begin);
|
||||
}
|
||||
|
||||
// mmap the section indicated by the offset (or malloc/fread on windows)
|
||||
#ifdef _WIN32
|
||||
blob = (void *)malloc(size);
|
||||
assert(blob != NULL);
|
||||
fseek(runtime, (long)offset, SEEK_SET);
|
||||
fread(blob, size, 1, runtime);
|
||||
#else
|
||||
blob = (void *)mmap(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fileno(runtime), offset);
|
||||
assert(blob != MAP_FAILED);
|
||||
#endif
|
||||
|
||||
fclose(runtime);
|
||||
return blob;
|
||||
}
|
||||
|
||||
/**
|
||||
* The inverse of map_blob.
|
||||
*/
|
||||
static void unmap_blob(void *blob) {
|
||||
if (blob) {
|
||||
#ifdef _WIN32
|
||||
free(blob);
|
||||
#else
|
||||
munmap(blob, blobinfo.blob_size);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Main entry point to deploy-stub.
|
||||
*/
|
||||
#ifdef _WIN32
|
||||
int wmain(int argc, wchar_t *argv[]) {
|
||||
#else
|
||||
int main(int argc, char *argv[]) {
|
||||
#endif
|
||||
int retval;
|
||||
ModuleDef *moddef;
|
||||
const char *log_filename;
|
||||
void *blob = NULL;
|
||||
log_filename = NULL;
|
||||
|
||||
#ifdef __APPLE__
|
||||
// Strip a -psn_xxx argument passed in by macOS when run from an .app bundle.
|
||||
if (argc > 1 && strncmp(argv[1], "-psn_", 5) == 0) {
|
||||
argv[1] = argv[0];
|
||||
++argv;
|
||||
--argc;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
printf("blob_offset: %d\n", (int)blobinfo.blob_offset);
|
||||
printf("blob_size: %d\n", (int)blobinfo.blob_size);
|
||||
printf("version: %d\n", (int)blobinfo.version);
|
||||
printf("num_pointers: %d\n", (int)blobinfo.num_pointers);
|
||||
printf("codepage: %d\n", (int)blobinfo.codepage);
|
||||
printf("flags: %d\n", (int)blobinfo.flags);
|
||||
printf("reserved: %d\n", (int)blobinfo.reserved);
|
||||
*/
|
||||
|
||||
// If we have a blob offset, we have to map the blob to memory.
|
||||
if (blobinfo.version == 0 || blobinfo.blob_offset != 0) {
|
||||
void *blob = map_blob((off_t)blobinfo.blob_offset, (size_t)blobinfo.blob_size);
|
||||
assert(blob != NULL);
|
||||
|
||||
// Offset the pointers in the header using the base mmap address.
|
||||
if (blobinfo.version > 0 && blobinfo.num_pointers > 0) {
|
||||
uint32_t i;
|
||||
assert(blobinfo.num_pointers <= MAX_NUM_POINTERS);
|
||||
for (i = 0; i < blobinfo.num_pointers; ++i) {
|
||||
// Only offset if the pointer is non-NULL. Except for the first
|
||||
// pointer, which may never be NULL and usually (but not always)
|
||||
// points to the beginning of the blob.
|
||||
if (i == 0 || blobinfo.pointers[i] != 0) {
|
||||
blobinfo.pointers[i] = (void *)((uintptr_t)blobinfo.pointers[i] + (uintptr_t)blob);
|
||||
}
|
||||
}
|
||||
if (blobinfo.num_pointers >= 12) {
|
||||
log_filename = blobinfo.pointers[11];
|
||||
}
|
||||
} else {
|
||||
blobinfo.pointers[0] = blob;
|
||||
}
|
||||
|
||||
// Offset the pointers in the module table using the base mmap address.
|
||||
moddef = blobinfo.pointers[0];
|
||||
#if PY_VERSION_HEX < 0x030b0000
|
||||
PyImport_FrozenModules = moddef;
|
||||
#endif
|
||||
while (moddef->name) {
|
||||
moddef->name = (char *)((uintptr_t)moddef->name + (uintptr_t)blob);
|
||||
if (moddef->code != 0) {
|
||||
moddef->code = (unsigned char *)((uintptr_t)moddef->code + (uintptr_t)blob);
|
||||
}
|
||||
//printf("MOD: %s %p %d\n", moddef->name, (void*)moddef->code, moddef->size);
|
||||
moddef++;
|
||||
}
|
||||
|
||||
// In Python 3.11, we need to convert this to the new structure format.
|
||||
#if PY_VERSION_HEX >= 0x030b0000
|
||||
ModuleDef *moddef_end = moddef;
|
||||
ptrdiff_t num_modules = moddef - (ModuleDef *)blobinfo.pointers[0];
|
||||
struct _frozen *new_moddef = (struct _frozen *)calloc(num_modules + 1, sizeof(struct _frozen));
|
||||
PyImport_FrozenModules = new_moddef;
|
||||
for (moddef = blobinfo.pointers[0]; moddef < moddef_end; ++moddef) {
|
||||
new_moddef->name = moddef->name;
|
||||
new_moddef->code = moddef->code;
|
||||
new_moddef->size = moddef->size < 0 ? -(moddef->size) : moddef->size;
|
||||
new_moddef->is_package = moddef->size < 0;
|
||||
#if PY_VERSION_HEX < 0x030d0000 // 3.13
|
||||
new_moddef->get_code = NULL;
|
||||
#endif
|
||||
new_moddef++;
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
PyImport_FrozenModules = blobinfo.pointers[0];
|
||||
}
|
||||
|
||||
if (log_filename != NULL) {
|
||||
char log_filename_buf[4096];
|
||||
if (blobinfo.flags & F_log_filename_strftime) {
|
||||
log_filename_buf[0] = 0;
|
||||
time_t now = time(NULL);
|
||||
if (strftime(log_filename_buf, sizeof(log_filename_buf), log_filename, localtime(&now)) > 0) {
|
||||
log_filename = log_filename_buf;
|
||||
}
|
||||
}
|
||||
setup_logging(log_filename, (blobinfo.flags & F_log_append) != 0);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
if (blobinfo.codepage != 0) {
|
||||
SetConsoleCP(blobinfo.codepage);
|
||||
SetConsoleOutputCP(blobinfo.codepage);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Run frozen application
|
||||
retval = Py_FrozenMain(argc, argv);
|
||||
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
|
||||
#if PY_VERSION_HEX >= 0x030b0000
|
||||
free((void *)PyImport_FrozenModules);
|
||||
PyImport_FrozenModules = NULL;
|
||||
#endif
|
||||
|
||||
unmap_blob(blob);
|
||||
return retval;
|
||||
}
|
||||
|
||||
#ifdef WIN_UNICODE
|
||||
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, wchar_t *lpCmdLine, int nCmdShow) {
|
||||
return wmain(__argc, __wargv);
|
||||
}
|
||||
#elif defined(_WIN32)
|
||||
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, char *lpCmdLine, int nCmdShow) {
|
||||
return main(__argc, __argv);
|
||||
}
|
||||
#endif
|
||||
@ -1,134 +0,0 @@
|
||||
/* FreezeDLLMain.cpp
|
||||
|
||||
This is a DLLMain suitable for frozen applications/DLLs on
|
||||
a Windows platform.
|
||||
|
||||
The general problem is that many Python extension modules may define
|
||||
DLL main functions, but when statically linked together to form
|
||||
a frozen application, this DLLMain symbol exists multiple times.
|
||||
|
||||
The solution is:
|
||||
* Each module checks for a frozen build, and if so, defines its DLLMain
|
||||
function as "__declspec(dllexport) DllMain%module%"
|
||||
(eg, DllMainpythoncom, or DllMainpywintypes)
|
||||
|
||||
* The frozen .EXE/.DLL links against this module, which provides
|
||||
the single DllMain.
|
||||
|
||||
* This DllMain attempts to locate and call the DllMain for each
|
||||
of the extension modules.
|
||||
|
||||
* This code also has hooks to "simulate" DllMain when used from
|
||||
a frozen .EXE.
|
||||
|
||||
At this stage, there is a static table of "possibly embedded modules".
|
||||
This should change to something better, but it will work OK for now.
|
||||
|
||||
Note that this scheme does not handle dependencies in the order
|
||||
of DllMain calls - except it does call pywintypes first :-)
|
||||
|
||||
As an example of how an extension module with a DllMain should be
|
||||
changed, here is a snippet from the pythoncom extension module.
|
||||
|
||||
// end of example code from pythoncom's DllMain.cpp
|
||||
#ifndef BUILD_FREEZE
|
||||
#define DLLMAIN DllMain
|
||||
#define DLLMAIN_DECL
|
||||
#else
|
||||
#define DLLMAIN DllMainpythoncom
|
||||
#define DLLMAIN_DECL __declspec(dllexport)
|
||||
#endif
|
||||
|
||||
extern "C" DLLMAIN_DECL
|
||||
BOOL WINAPI DLLMAIN(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
|
||||
// end of example code from pythoncom's DllMain.cpp
|
||||
|
||||
***************************************************************************/
|
||||
#include "windows.h"
|
||||
|
||||
static char *possibleModules[] = {
|
||||
"pywintypes",
|
||||
"pythoncom",
|
||||
"win32ui",
|
||||
NULL,
|
||||
};
|
||||
|
||||
BOOL CallModuleDllMain(char *modName, DWORD dwReason);
|
||||
|
||||
|
||||
/*
|
||||
Called by a frozen .EXE only, so that built-in extension
|
||||
modules are initialized correctly
|
||||
*/
|
||||
void PyWinFreeze_ExeInit(void)
|
||||
{
|
||||
char **modName;
|
||||
for (modName = possibleModules;*modName;*modName++) {
|
||||
/* printf("Initialising '%s'\n", *modName); */
|
||||
CallModuleDllMain(*modName, DLL_PROCESS_ATTACH);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Called by a frozen .EXE only, so that built-in extension
|
||||
modules are cleaned up
|
||||
*/
|
||||
void PyWinFreeze_ExeTerm(void)
|
||||
{
|
||||
// Must go backwards
|
||||
char **modName;
|
||||
for (modName = possibleModules+(sizeof(possibleModules) / sizeof(char *))-2;
|
||||
modName >= possibleModules;
|
||||
*modName--) {
|
||||
/* printf("Terminating '%s'\n", *modName);*/
|
||||
CallModuleDllMain(*modName, DLL_PROCESS_DETACH);
|
||||
}
|
||||
}
|
||||
|
||||
BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
|
||||
{
|
||||
BOOL ret = TRUE;
|
||||
switch (dwReason) {
|
||||
case DLL_PROCESS_ATTACH:
|
||||
{
|
||||
char **modName;
|
||||
for (modName = possibleModules;*modName;*modName++) {
|
||||
BOOL ok = CallModuleDllMain(*modName, dwReason);
|
||||
if (!ok)
|
||||
ret = FALSE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DLL_PROCESS_DETACH:
|
||||
{
|
||||
// Must go backwards
|
||||
char **modName;
|
||||
for (modName = possibleModules+(sizeof(possibleModules) / sizeof(char *))-2;
|
||||
modName >= possibleModules;
|
||||
*modName--)
|
||||
CallModuleDllMain(*modName, DLL_PROCESS_DETACH);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
BOOL CallModuleDllMain(char *modName, DWORD dwReason)
|
||||
{
|
||||
BOOL (WINAPI * pfndllmain)(HINSTANCE, DWORD, LPVOID);
|
||||
|
||||
char funcName[255];
|
||||
HMODULE hmod = GetModuleHandleW(NULL);
|
||||
strcpy(funcName, "_DllMain");
|
||||
strcat(funcName, modName);
|
||||
strcat(funcName, "@12"); // stdcall convention.
|
||||
pfndllmain = (BOOL (WINAPI *)(HINSTANCE, DWORD, LPVOID))GetProcAddress(hmod, funcName);
|
||||
if (pfndllmain==NULL) {
|
||||
/* No function by that name exported - then that module does
|
||||
not appear in our frozen program - return OK
|
||||
*/
|
||||
return TRUE;
|
||||
}
|
||||
return (*pfndllmain)(hmod, dwReason, NULL);
|
||||
}
|
||||
|
||||
@ -1,20 +0,0 @@
|
||||
set(P3DXF_HEADERS
|
||||
dxfFile.h
|
||||
dxfLayer.h
|
||||
dxfLayerMap.h
|
||||
dxfVertex.h
|
||||
)
|
||||
|
||||
set(P3DXF_SOURCES
|
||||
dxfFile.cxx
|
||||
dxfLayer.cxx
|
||||
dxfLayerMap.cxx
|
||||
dxfVertex.cxx
|
||||
)
|
||||
|
||||
composite_sources(p3dxf P3DXF_SOURCES)
|
||||
add_library(p3dxf STATIC ${P3DXF_HEADERS} ${P3DXF_SOURCES})
|
||||
target_link_libraries(p3dxf p3pandatoolbase)
|
||||
|
||||
# This is only needed for binaries in the pandatool package. It is not useful
|
||||
# for user applications, so it is not installed.
|
||||
@ -1,937 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file dxfFile.cxx
|
||||
* @author drose
|
||||
* @date 2004-05-04
|
||||
*/
|
||||
|
||||
#include "dxfFile.h"
|
||||
#include "string_utils.h"
|
||||
#include "virtualFileSystem.h"
|
||||
|
||||
using std::istream;
|
||||
using std::ostream;
|
||||
using std::string;
|
||||
|
||||
DXFFile::Color DXFFile::_colors[DXF_num_colors] = {
|
||||
{ 1, 1, 1 }, // Color 0 is not used.
|
||||
{ 1, 0, 0 }, // Color 1 = Red
|
||||
{ 1, 1, 0 }, // Color 2 = Yellow
|
||||
{ 0, 1, 0 }, // Color 3 = Green
|
||||
{ 0, 1, 1 }, // Color 4 = Cyan
|
||||
{ 0, 0, 1 }, // Color 5 = Blue
|
||||
{ 1, 0, 1 }, // Color 6 = Magenta
|
||||
{ 1, 1, 1 }, // Color 7 = Black/White
|
||||
{ 0.3, 0.3, 0.3 }, // Color 8 = Gray
|
||||
{ 0.7, 0.7, 0.7 }, // Color 9 = Gray
|
||||
{ 1, 0, 0 }, // Remaining colors are from the fancy palette.
|
||||
{ 1, 0.5, 0.5 },
|
||||
{ 0.65, 0, 0 },
|
||||
{ 0.65, 0.325, 0.325 },
|
||||
{ 0.5, 0, 0 },
|
||||
{ 0.5, 0.25, 0.25 },
|
||||
{ 0.3, 0, 0 },
|
||||
{ 0.3, 0.15, 0.15 },
|
||||
{ 0.15, 0, 0 },
|
||||
{ 0.15, 0.075, 0.075 },
|
||||
{ 1, 0.25, 0 },
|
||||
{ 1, 0.625, 0.5 },
|
||||
{ 0.65, 0.1625, 0 },
|
||||
{ 0.65, 0.4063, 0.325 },
|
||||
{ 0.5, 0.125, 0 },
|
||||
{ 0.5, 0.3125, 0.25 },
|
||||
{ 0.3, 0.075, 0 },
|
||||
{ 0.3, 0.1875, 0.15 },
|
||||
{ 0.15, 0.0375, 0 },
|
||||
{ 0.15, 0.0938, 0.075 },
|
||||
{ 1, 0.5, 0 },
|
||||
{ 1, 0.75, 0.5 },
|
||||
{ 0.65, 0.325, 0 },
|
||||
{ 0.65, 0.4875, 0.325 },
|
||||
{ 0.5, 0.25, 0 },
|
||||
{ 0.5, 0.375, 0.25 },
|
||||
{ 0.3, 0.15, 0 },
|
||||
{ 0.3, 0.225, 0.15 },
|
||||
{ 0.15, 0.075, 0 },
|
||||
{ 0.15, 0.1125, 0.075 },
|
||||
{ 1, 0.75, 0 },
|
||||
{ 1, 0.875, 0.5 },
|
||||
{ 0.65, 0.4875, 0 },
|
||||
{ 0.65, 0.5688, 0.325 },
|
||||
{ 0.5, 0.375, 0 },
|
||||
{ 0.5, 0.4375, 0.25 },
|
||||
{ 0.3, 0.225, 0 },
|
||||
{ 0.3, 0.2625, 0.15 },
|
||||
{ 0.15, 0.1125, 0 },
|
||||
{ 0.15, 0.1313, 0.075 },
|
||||
{ 1, 1, 0 },
|
||||
{ 1, 1, 0.5 },
|
||||
{ 0.65, 0.65, 0 },
|
||||
{ 0.65, 0.65, 0.325 },
|
||||
{ 0.5, 0.5, 0 },
|
||||
{ 0.5, 0.5, 0.25 },
|
||||
{ 0.3, 0.3, 0 },
|
||||
{ 0.3, 0.3, 0.15 },
|
||||
{ 0.15, 0.15, 0 },
|
||||
{ 0.15, 0.15, 0.075 },
|
||||
{ 0.75, 1, 0 },
|
||||
{ 0.875, 1, 0.5 },
|
||||
{ 0.4875, 0.65, 0 },
|
||||
{ 0.5688, 0.65, 0.325 },
|
||||
{ 0.375, 0.5, 0 },
|
||||
{ 0.4375, 0.5, 0.25 },
|
||||
{ 0.225, 0.3, 0 },
|
||||
{ 0.2625, 0.3, 0.15 },
|
||||
{ 0.1125, 0.15, 0 },
|
||||
{ 0.1313, 0.15, 0.075 },
|
||||
{ 0.5, 1, 0 },
|
||||
{ 0.75, 1, 0.5 },
|
||||
{ 0.325, 0.65, 0 },
|
||||
{ 0.4875, 0.65, 0.325 },
|
||||
{ 0.25, 0.5, 0 },
|
||||
{ 0.375, 0.5, 0.25 },
|
||||
{ 0.15, 0.3, 0 },
|
||||
{ 0.225, 0.3, 0.15 },
|
||||
{ 0.075, 0.15, 0 },
|
||||
{ 0.1125, 0.15, 0.075 },
|
||||
{ 0.25, 1, 0 },
|
||||
{ 0.625, 1, 0.5 },
|
||||
{ 0.1625, 0.65, 0 },
|
||||
{ 0.4063, 0.65, 0.325 },
|
||||
{ 0.125, 0.5, 0 },
|
||||
{ 0.3125, 0.5, 0.25 },
|
||||
{ 0.075, 0.3, 0 },
|
||||
{ 0.1875, 0.3, 0.15 },
|
||||
{ 0.0375, 0.15, 0 },
|
||||
{ 0.0938, 0.15, 0.075 },
|
||||
{ 0, 1, 0 },
|
||||
{ 0.5, 1, 0.5 },
|
||||
{ 0, 0.65, 0 },
|
||||
{ 0.325, 0.65, 0.325 },
|
||||
{ 0, 0.5, 0 },
|
||||
{ 0.25, 0.5, 0.25 },
|
||||
{ 0, 0.3, 0 },
|
||||
{ 0.15, 0.3, 0.15 },
|
||||
{ 0, 0.15, 0 },
|
||||
{ 0.075, 0.15, 0.075 },
|
||||
{ 0, 1, 0.25 },
|
||||
{ 0.5, 1, 0.625 },
|
||||
{ 0, 0.65, 0.1625 },
|
||||
{ 0.325, 0.65, 0.4063 },
|
||||
{ 0, 0.5, 0.125 },
|
||||
{ 0.25, 0.5, 0.3125 },
|
||||
{ 0, 0.3, 0.075 },
|
||||
{ 0.15, 0.3, 0.1875 },
|
||||
{ 0, 0.15, 0.0375 },
|
||||
{ 0.075, 0.15, 0.0938 },
|
||||
{ 0, 1, 0.5 },
|
||||
{ 0.5, 1, 0.75 },
|
||||
{ 0, 0.65, 0.325 },
|
||||
{ 0.325, 0.65, 0.4875 },
|
||||
{ 0, 0.5, 0.25 },
|
||||
{ 0.25, 0.5, 0.375 },
|
||||
{ 0, 0.3, 0.15 },
|
||||
{ 0.15, 0.3, 0.225 },
|
||||
{ 0, 0.15, 0.075 },
|
||||
{ 0.075, 0.15, 0.1125 },
|
||||
{ 0, 1, 0.75 },
|
||||
{ 0.5, 1, 0.875 },
|
||||
{ 0, 0.65, 0.4875 },
|
||||
{ 0.325, 0.65, 0.5688 },
|
||||
{ 0, 0.5, 0.375 },
|
||||
{ 0.25, 0.5, 0.4375 },
|
||||
{ 0, 0.3, 0.225 },
|
||||
{ 0.15, 0.3, 0.2625 },
|
||||
{ 0, 0.15, 0.1125 },
|
||||
{ 0.075, 0.15, 0.1313 },
|
||||
{ 0, 1, 1 },
|
||||
{ 0.5, 1, 1 },
|
||||
{ 0, 0.65, 0.65 },
|
||||
{ 0.325, 0.65, 0.65 },
|
||||
{ 0, 0.5, 0.5 },
|
||||
{ 0.25, 0.5, 0.5 },
|
||||
{ 0, 0.3, 0.3 },
|
||||
{ 0.15, 0.3, 0.3 },
|
||||
{ 0, 0.15, 0.15 },
|
||||
{ 0.075, 0.15, 0.15 },
|
||||
{ 0, 0.75, 1 },
|
||||
{ 0.5, 0.875, 1 },
|
||||
{ 0, 0.4875, 0.65 },
|
||||
{ 0.325, 0.5688, 0.65 },
|
||||
{ 0, 0.375, 0.5 },
|
||||
{ 0.25, 0.4375, 0.5 },
|
||||
{ 0, 0.225, 0.3 },
|
||||
{ 0.15, 0.2625, 0.3 },
|
||||
{ 0, 0.1125, 0.15 },
|
||||
{ 0.075, 0.1313, 0.15 },
|
||||
{ 0, 0.5, 1 },
|
||||
{ 0.5, 0.75, 1 },
|
||||
{ 0, 0.325, 0.65 },
|
||||
{ 0.325, 0.4875, 0.65 },
|
||||
{ 0, 0.25, 0.5 },
|
||||
{ 0.25, 0.375, 0.5 },
|
||||
{ 0, 0.15, 0.3 },
|
||||
{ 0.15, 0.225, 0.3 },
|
||||
{ 0, 0.075, 0.15 },
|
||||
{ 0.075, 0.1125, 0.15 },
|
||||
{ 0, 0.25, 1 },
|
||||
{ 0.5, 0.625, 1 },
|
||||
{ 0, 0.1625, 0.65 },
|
||||
{ 0.325, 0.4063, 0.65 },
|
||||
{ 0, 0.125, 0.5 },
|
||||
{ 0.25, 0.3125, 0.5 },
|
||||
{ 0, 0.075, 0.3 },
|
||||
{ 0.15, 0.1875, 0.3 },
|
||||
{ 0, 0.0375, 0.15 },
|
||||
{ 0.075, 0.0938, 0.15 },
|
||||
{ 0, 0, 1 },
|
||||
{ 0.5, 0.5, 1 },
|
||||
{ 0, 0, 0.65 },
|
||||
{ 0.325, 0.325, 0.65 },
|
||||
{ 0, 0, 0.5 },
|
||||
{ 0.25, 0.25, 0.5 },
|
||||
{ 0, 0, 0.3 },
|
||||
{ 0.15, 0.15, 0.3 },
|
||||
{ 0, 0, 0.15 },
|
||||
{ 0.075, 0.075, 0.15 },
|
||||
{ 0.25, 0, 1 },
|
||||
{ 0.625, 0.5, 1 },
|
||||
{ 0.1625, 0, 0.65 },
|
||||
{ 0.4063, 0.325, 0.65 },
|
||||
{ 0.125, 0, 0.5 },
|
||||
{ 0.3125, 0.25, 0.5 },
|
||||
{ 0.075, 0, 0.3 },
|
||||
{ 0.1875, 0.15, 0.3 },
|
||||
{ 0.0375, 0, 0.15 },
|
||||
{ 0.0938, 0.075, 0.15 },
|
||||
{ 0.5, 0, 1 },
|
||||
{ 0.75, 0.5, 1 },
|
||||
{ 0.325, 0, 0.65 },
|
||||
{ 0.4875, 0.325, 0.65 },
|
||||
{ 0.25, 0, 0.5 },
|
||||
{ 0.375, 0.25, 0.5 },
|
||||
{ 0.15, 0, 0.3 },
|
||||
{ 0.225, 0.15, 0.3 },
|
||||
{ 0.075, 0, 0.15 },
|
||||
{ 0.1125, 0.075, 0.15 },
|
||||
{ 0.75, 0, 1 },
|
||||
{ 0.875, 0.5, 1 },
|
||||
{ 0.4875, 0, 0.65 },
|
||||
{ 0.5688, 0.325, 0.65 },
|
||||
{ 0.375, 0, 0.5 },
|
||||
{ 0.4375, 0.25, 0.5 },
|
||||
{ 0.225, 0, 0.3 },
|
||||
{ 0.2625, 0.15, 0.3 },
|
||||
{ 0.1125, 0, 0.15 },
|
||||
{ 0.1313, 0.075, 0.15 },
|
||||
{ 1, 0, 1 },
|
||||
{ 1, 0.5, 1 },
|
||||
{ 0.65, 0, 0.65 },
|
||||
{ 0.65, 0.325, 0.65 },
|
||||
{ 0.5, 0, 0.5 },
|
||||
{ 0.5, 0.25, 0.5 },
|
||||
{ 0.3, 0, 0.3 },
|
||||
{ 0.3, 0.15, 0.3 },
|
||||
{ 0.15, 0, 0.15 },
|
||||
{ 0.15, 0.075, 0.15 },
|
||||
{ 1, 0, 0.75 },
|
||||
{ 1, 0.5, 0.875 },
|
||||
{ 0.65, 0, 0.4875 },
|
||||
{ 0.65, 0.325, 0.5688 },
|
||||
{ 0.5, 0, 0.375 },
|
||||
{ 0.5, 0.25, 0.4375 },
|
||||
{ 0.3, 0, 0.225 },
|
||||
{ 0.3, 0.15, 0.2625 },
|
||||
{ 0.15, 0, 0.1125 },
|
||||
{ 0.15, 0.075, 0.1313 },
|
||||
{ 1, 0, 0.5 },
|
||||
{ 1, 0.5, 0.75 },
|
||||
{ 0.65, 0, 0.325 },
|
||||
{ 0.65, 0.325, 0.4875 },
|
||||
{ 0.5, 0, 0.25 },
|
||||
{ 0.5, 0.25, 0.375 },
|
||||
{ 0.3, 0, 0.15 },
|
||||
{ 0.3, 0.15, 0.225 },
|
||||
{ 0.15, 0, 0.075 },
|
||||
{ 0.15, 0.075, 0.1125 },
|
||||
{ 1, 0, 0.25 },
|
||||
{ 1, 0.5, 0.625 },
|
||||
{ 0.65, 0, 0.1625 },
|
||||
{ 0.65, 0.325, 0.4063 },
|
||||
{ 0.5, 0, 0.125 },
|
||||
{ 0.5, 0.25, 0.3125 },
|
||||
{ 0.3, 0, 0.075 },
|
||||
{ 0.3, 0.15, 0.1875 },
|
||||
{ 0.15, 0, 0.0375 },
|
||||
{ 0.15, 0.075, 0.0938 },
|
||||
{ 0.33, 0.33, 0.33 },
|
||||
{ 0.464, 0.464, 0.464 },
|
||||
{ 0.598, 0.598, 0.598 },
|
||||
{ 0.732, 0.732, 0.732 },
|
||||
{ 0.866, 0.866, 0.866 },
|
||||
{ 1, 1, 1 },
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
DXFFile::
|
||||
DXFFile() {
|
||||
_in = nullptr;
|
||||
_owns_in = false;
|
||||
_layer = nullptr;
|
||||
reset_entity();
|
||||
_color_index = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
DXFFile::
|
||||
~DXFFile() {
|
||||
if (_owns_in) {
|
||||
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
|
||||
vfs->close_read_file(_in);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Opens the indicated filename and reads it as a DXF file.
|
||||
*/
|
||||
void DXFFile::
|
||||
process(Filename filename) {
|
||||
filename.set_text();
|
||||
|
||||
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
|
||||
istream *in = vfs->open_read_file(filename, true);
|
||||
if (in == nullptr) {
|
||||
return;
|
||||
}
|
||||
process(in, true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reads the indicated stream as a DXF file. If owns_in is true, then the
|
||||
* istream will be deleted via vfs->close_read_file() when the DXFFile object
|
||||
* destructs.
|
||||
*/
|
||||
void DXFFile::
|
||||
process(istream *in, bool owns_in) {
|
||||
if (_owns_in) {
|
||||
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
|
||||
vfs->close_read_file(_in);
|
||||
}
|
||||
_in = in;
|
||||
_owns_in = owns_in;
|
||||
_state = ST_top;
|
||||
|
||||
begin_file();
|
||||
while (_state != ST_done && _state != ST_error) {
|
||||
if (get_group()) {
|
||||
switch (_state) {
|
||||
case ST_top:
|
||||
state_top();
|
||||
break;
|
||||
|
||||
case ST_section:
|
||||
state_section();
|
||||
break;
|
||||
|
||||
case ST_entity:
|
||||
state_entity();
|
||||
break;
|
||||
|
||||
case ST_verts:
|
||||
state_verts();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A hook for user code, if desired. This function is called whenever
|
||||
* processing begins on the DXF file.
|
||||
*/
|
||||
void DXFFile::
|
||||
begin_file() {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A hook for user code, if desired. This function is called whenever a new
|
||||
* section in the DXF file is encountered.
|
||||
*/
|
||||
void DXFFile::
|
||||
begin_section() {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A hook for user code, if desired. This function is called whenever a
|
||||
* vertex is read from the DXF file. This function has the default behavior
|
||||
* of adding the vertex to the _verts list, so that when done_entity() is
|
||||
* called later, it will have the complete list of vertices available to it.
|
||||
*/
|
||||
void DXFFile::
|
||||
done_vertex() {
|
||||
DXFVertex v;
|
||||
v._p = _p;
|
||||
_verts.push_back(v);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This is the primary hook for user code. This function is called when an
|
||||
* entity is read from the DXF file. This may be something like a polygon,
|
||||
* point, or a polygon mesh: any geometry. It is up to the user code to
|
||||
* override this function and do something interesting with each piece of
|
||||
* geometry that is read.
|
||||
*/
|
||||
void DXFFile::
|
||||
done_entity() {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A hook for user code, if desired. This function is called as each section
|
||||
* in the DXF file is finished.
|
||||
*/
|
||||
void DXFFile::
|
||||
end_section() {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A hook for user code, if desired. This function is called when the DXF
|
||||
* processing is complete.
|
||||
*/
|
||||
void DXFFile::
|
||||
end_file() {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A hook for user code, if desired. This function is called when some
|
||||
* unexpected error occurs while reading the DXF file.
|
||||
*/
|
||||
void DXFFile::
|
||||
error() {
|
||||
nout << "Error!\n";
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Returns the index of the closest matching AutoCAD color to the indicated r,
|
||||
* g, b.
|
||||
*/
|
||||
int DXFFile::
|
||||
find_color(double r, double g, double b) {
|
||||
double best_diff = 4.0; // 4 is greater than our expected max, 3.
|
||||
int best_index = 7;
|
||||
|
||||
for (int i = 0; i < 255; i++) {
|
||||
double diff = ((r - _colors[i].r) * (r - _colors[i].r) +
|
||||
(g - _colors[i].g) * (g - _colors[i].g) +
|
||||
(b - _colors[i].b) * (b - _colors[i].b));
|
||||
if (diff < best_diff) {
|
||||
best_diff = diff;
|
||||
best_index = i;
|
||||
}
|
||||
}
|
||||
|
||||
return best_index;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a convenience function to return the r,g,b color of the current
|
||||
* entity (at the time of done_entity()). It's based on the _color_index
|
||||
* value that was read from the DXF file.
|
||||
*/
|
||||
const DXFFile::Color &DXFFile::
|
||||
get_color() const {
|
||||
if (_color_index >= 0 && _color_index <= 255) {
|
||||
return _colors[_color_index];
|
||||
}
|
||||
return _colors[0];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Assuming the current entity is a planar-based entity, for instance, a 2-d
|
||||
* polygon (as opposed to a 3-d polygon), this converts the coordinates from
|
||||
* the funny planar coordinate system to the world coordinates. It converts
|
||||
* the _p value of the entity, as well as all vertices in the _verts list.
|
||||
*/
|
||||
void DXFFile::
|
||||
ocs_2_wcs() {
|
||||
compute_ocs();
|
||||
|
||||
// Convert the entity's position.
|
||||
_p = _p * _ocs2wcs;
|
||||
|
||||
// Maybe we have these coordinates too.
|
||||
_q = _q * _ocs2wcs;
|
||||
_r = _r * _ocs2wcs;
|
||||
_s = _s * _ocs2wcs;
|
||||
|
||||
// If there are any vertices, convert them too.
|
||||
DXFVertices::iterator vi;
|
||||
for (vi = _verts.begin(); vi != _verts.end(); ++vi) {
|
||||
(*vi)._p = (*vi)._p * _ocs2wcs;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Computes the matrix used to convert from the planar coordinate system to
|
||||
* world coordinates.
|
||||
*/
|
||||
void DXFFile::
|
||||
compute_ocs() {
|
||||
// A 2-d entity's vertices might be defined in an "Object Coordinate System"
|
||||
// which has a funny definition. Its Z axis is defined by _z, and its X and
|
||||
// Y axes are inferred from that. The origin is the same as the world
|
||||
// coordinate system's origin.
|
||||
|
||||
// The Z axis is _z. Determine the x and y axes.
|
||||
LVector3d x, y;
|
||||
|
||||
if (fabs(_z[0]) < 1.0/64.0 && fabs(_z[1]) < 1.0/64.0) {
|
||||
x = cross(LVector3d(0.0, 1.0, 0.0), _z);
|
||||
} else {
|
||||
x = cross(LVector3d(0.0, 0.0, 1.0), _z);
|
||||
}
|
||||
x.normalize();
|
||||
y = cross(x, _z);
|
||||
y.normalize();
|
||||
|
||||
// Now build a rotate matrix from these vectors.
|
||||
LMatrix4d
|
||||
ocs( x[0], x[1], x[2], 0,
|
||||
y[0], y[1], y[2], 0,
|
||||
_z[0], _z[1], _z[2], 0,
|
||||
0, 0, 0, 1);
|
||||
|
||||
_ocs2wcs.invert_from(ocs);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reads the next code, string pair from the DXF file. This is the basic unit
|
||||
* of data in a DXF file.
|
||||
*/
|
||||
bool DXFFile::
|
||||
get_group() {
|
||||
istream &in = *_in;
|
||||
do {
|
||||
in >> _code;
|
||||
if (!in) {
|
||||
change_state(ST_error);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now skip past exactly one newline character and any number of other
|
||||
// whitespace characters.
|
||||
while (in && in.peek() != '\n') {
|
||||
in.get();
|
||||
}
|
||||
in.get();
|
||||
while (in && isspace(in.peek()) && in.peek() != '\n') {
|
||||
in.get();
|
||||
}
|
||||
|
||||
std::getline(in, _string);
|
||||
_string = trim_right(_string);
|
||||
|
||||
if (!in) {
|
||||
change_state(ST_error);
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we just read a comment, go back and get another one.
|
||||
} while (_code == 999);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called as new nodes are read to update the internal state correctly.
|
||||
*/
|
||||
void DXFFile::
|
||||
change_state(State new_state) {
|
||||
if (_state == ST_verts) {
|
||||
done_vertex();
|
||||
_p.set(0.0, 0.0, 0.0);
|
||||
_q.set(0.0, 0.0, 0.0);
|
||||
_r.set(0.0, 0.0, 0.0);
|
||||
_s.set(0.0, 0.0, 0.0);
|
||||
}
|
||||
if ((_state == ST_entity || _state == ST_verts) &&
|
||||
new_state != ST_verts) {
|
||||
// We finish an entity when we read a new entity, or when we've read the
|
||||
// last vertex (if we were scanning the vertices after an entity).
|
||||
done_entity();
|
||||
reset_entity();
|
||||
}
|
||||
switch (new_state) {
|
||||
case ST_top:
|
||||
end_section();
|
||||
break;
|
||||
|
||||
case ST_done:
|
||||
end_file();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
_state = new_state;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void DXFFile::
|
||||
change_section(Section new_section) {
|
||||
change_state(ST_section);
|
||||
_section = new_section;
|
||||
begin_section();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Given a newly read layer name, sets the _layer pointer to point to the
|
||||
* associate layer. If the layer name has not been encountered before,
|
||||
* creates a new layer definition.
|
||||
*/
|
||||
void DXFFile::
|
||||
change_layer(const string &layer_name) {
|
||||
if (_layer == nullptr || _layer->get_name() != layer_name) {
|
||||
_layer = _layers.get_layer(layer_name, this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void DXFFile::
|
||||
change_entity(Entity new_entity) {
|
||||
if (new_entity == EN_vertex && _vertices_follow) {
|
||||
// If we read a new vertex and we're still scanning the vertices that
|
||||
// follow an entity, keep scanning it--we haven't finished the entity yet.
|
||||
change_state(ST_verts);
|
||||
|
||||
} else {
|
||||
// Otherwise, begin a new entity.
|
||||
change_state(ST_entity);
|
||||
_entity = new_entity;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Resets the current entity to its initial, default state prior to reading a
|
||||
* new entity.
|
||||
*/
|
||||
void DXFFile::
|
||||
reset_entity() {
|
||||
_p.set(0.0, 0.0, 0.0);
|
||||
_q.set(0.0, 0.0, 0.0);
|
||||
_r.set(0.0, 0.0, 0.0);
|
||||
_s.set(0.0, 0.0, 0.0);
|
||||
_z.set(0.0, 0.0, 1.0);
|
||||
_vertices_follow = false;
|
||||
// _color_index = -1;
|
||||
|
||||
_verts.erase(_verts.begin(), _verts.end());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Does the DXF processing when we are at the top of the file, outside of any
|
||||
* section.
|
||||
*/
|
||||
void DXFFile::
|
||||
state_top() {
|
||||
if (_code != 0) {
|
||||
nout << "Group code 0 not found at top level; found code " << _code
|
||||
<< " instead.\n";
|
||||
change_state(ST_error);
|
||||
} else {
|
||||
if (_string == "SECTION") {
|
||||
if (get_group()) {
|
||||
if (_code != 2) {
|
||||
nout << "Group code 0 not immediately followed by code 2; found code "
|
||||
<< _code << " instead.\n";
|
||||
} else {
|
||||
if (_string == "HEADER") {
|
||||
change_section(SE_header);
|
||||
} else if (_string == "TABLES") {
|
||||
change_section(SE_tables);
|
||||
} else if (_string == "BLOCKS") {
|
||||
change_section(SE_blocks);
|
||||
} else if (_string == "ENTITIES") {
|
||||
change_section(SE_entities);
|
||||
} else if (_string == "OBJECTS") {
|
||||
change_section(SE_objects);
|
||||
} else {
|
||||
change_section(SE_unknown);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (_string == "EOF") {
|
||||
change_state(ST_done);
|
||||
} else {
|
||||
nout << "Unexpected section at top level: '" << _string << "'\n";
|
||||
change_state(ST_error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Does the DXF processing when we are within some section.
|
||||
*/
|
||||
void DXFFile::
|
||||
state_section() {
|
||||
string tail;
|
||||
|
||||
switch (_code) {
|
||||
case 0:
|
||||
if (_string == "ENDSEC") {
|
||||
change_state(ST_top);
|
||||
} else {
|
||||
if (_section == SE_entities) {
|
||||
if (_string == "3DFACE") {
|
||||
change_entity(EN_3dface);
|
||||
} else if (_string == "POINT") {
|
||||
change_entity(EN_point);
|
||||
} else if (_string == "INSERT") {
|
||||
change_entity(EN_insert);
|
||||
} else if (_string == "VERTEX") {
|
||||
change_entity(EN_vertex);
|
||||
} else if (_string == "POLYLINE") {
|
||||
change_entity(EN_polyline);
|
||||
} else {
|
||||
change_entity(EN_unknown);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 8:
|
||||
change_layer(_string);
|
||||
break;
|
||||
|
||||
case 62: // Color.
|
||||
_color_index = string_to_int(_string, tail);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Does the DXF processing when we are reading an entity.
|
||||
*/
|
||||
void DXFFile::
|
||||
state_entity() {
|
||||
string tail;
|
||||
|
||||
switch (_code) {
|
||||
case 0:
|
||||
state_section();
|
||||
break;
|
||||
|
||||
case 8:
|
||||
change_layer(_string);
|
||||
break;
|
||||
|
||||
case 10:
|
||||
_p[0] = string_to_double(_string, tail);
|
||||
break;
|
||||
|
||||
case 11:
|
||||
_q[0] = string_to_double(_string, tail);
|
||||
break;
|
||||
|
||||
case 12:
|
||||
_r[0] = string_to_double(_string, tail);
|
||||
break;
|
||||
|
||||
case 13:
|
||||
_s[0] = string_to_double(_string, tail);
|
||||
break;
|
||||
|
||||
case 20:
|
||||
_p[1] = string_to_double(_string, tail);
|
||||
break;
|
||||
|
||||
case 21:
|
||||
_q[1] = string_to_double(_string, tail);
|
||||
break;
|
||||
|
||||
case 22:
|
||||
_r[1] = string_to_double(_string, tail);
|
||||
break;
|
||||
|
||||
case 23:
|
||||
_s[1] = string_to_double(_string, tail);
|
||||
break;
|
||||
|
||||
case 30:
|
||||
_p[2] = string_to_double(_string, tail);
|
||||
break;
|
||||
|
||||
case 31:
|
||||
_q[2] = string_to_double(_string, tail);
|
||||
break;
|
||||
|
||||
case 32:
|
||||
_r[2] = string_to_double(_string, tail);
|
||||
break;
|
||||
|
||||
case 33:
|
||||
_s[2] = string_to_double(_string, tail);
|
||||
break;
|
||||
|
||||
case 62: // Color.
|
||||
_color_index = string_to_int(_string, tail);
|
||||
break;
|
||||
|
||||
case 66: // Vertices-follow.
|
||||
_vertices_follow = (string_to_int(_string, tail) != 0);
|
||||
break;
|
||||
|
||||
case 70: // Polyline flags.
|
||||
_flags = string_to_int(_string, tail);
|
||||
break;
|
||||
|
||||
case 210:
|
||||
_z[0] = string_to_double(_string, tail);
|
||||
break;
|
||||
|
||||
case 220:
|
||||
_z[1] = string_to_double(_string, tail);
|
||||
break;
|
||||
|
||||
case 230:
|
||||
_z[2] = string_to_double(_string, tail);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Does the DXF processing when we are reading the list of vertices that might
|
||||
* follow an entity.
|
||||
*/
|
||||
void DXFFile::
|
||||
state_verts() {
|
||||
string tail;
|
||||
|
||||
switch (_code) {
|
||||
case 0:
|
||||
state_section();
|
||||
break;
|
||||
|
||||
case 8:
|
||||
change_layer(_string);
|
||||
break;
|
||||
|
||||
case 10:
|
||||
_p[0] = string_to_double(_string, tail);
|
||||
break;
|
||||
|
||||
case 20:
|
||||
_p[1] = string_to_double(_string, tail);
|
||||
break;
|
||||
|
||||
case 30:
|
||||
_p[2] = string_to_double(_string, tail);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ostream &operator << (ostream &out, const DXFFile::State &state) {
|
||||
switch (state) {
|
||||
case DXFFile::ST_top:
|
||||
return out << "ST_top";
|
||||
case DXFFile::ST_section:
|
||||
return out << "ST_section";
|
||||
case DXFFile::ST_entity:
|
||||
return out << "ST_entity";
|
||||
case DXFFile::ST_verts:
|
||||
return out << "ST_verts";
|
||||
case DXFFile::ST_error:
|
||||
return out << "ST_error";
|
||||
case DXFFile::ST_done:
|
||||
return out << "ST_done";
|
||||
}
|
||||
return out << "Unknown state";
|
||||
}
|
||||
|
||||
ostream &operator << (ostream &out, const DXFFile::Section §ion) {
|
||||
switch (section) {
|
||||
case DXFFile::SE_unknown:
|
||||
return out << "SE_unknown";
|
||||
case DXFFile::SE_header:
|
||||
return out << "SE_header";
|
||||
case DXFFile::SE_tables:
|
||||
return out << "SE_tables";
|
||||
case DXFFile::SE_blocks:
|
||||
return out << "SE_blocks";
|
||||
case DXFFile::SE_entities:
|
||||
return out << "SE_entities";
|
||||
case DXFFile::SE_objects:
|
||||
return out << "SE_objects";
|
||||
}
|
||||
return out << "Unknown section";
|
||||
}
|
||||
|
||||
ostream &operator << (ostream &out, const DXFFile::Entity &entity) {
|
||||
switch (entity) {
|
||||
case DXFFile::EN_unknown:
|
||||
return out << "EN_unknown";
|
||||
case DXFFile::EN_3dface:
|
||||
return out << "EN_3dface";
|
||||
case DXFFile::EN_point:
|
||||
return out << "EN_point";
|
||||
case DXFFile::EN_insert:
|
||||
return out << "EN_insert";
|
||||
case DXFFile::EN_vertex:
|
||||
return out << "EN_vertex";
|
||||
case DXFFile::EN_polyline:
|
||||
return out << "EN_polyline";
|
||||
}
|
||||
return out << "Unknown entity";
|
||||
}
|
||||
@ -1,168 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file dxfFile.h
|
||||
* @author drose
|
||||
* @date 2004-05-04
|
||||
*/
|
||||
|
||||
#ifndef DXFFILE_H
|
||||
#define DXFFILE_H
|
||||
|
||||
#include "pandatoolbase.h"
|
||||
|
||||
#include "dxfLayer.h"
|
||||
#include "dxfLayerMap.h"
|
||||
#include "dxfVertex.h"
|
||||
|
||||
#include "luse.h"
|
||||
#include "filename.h"
|
||||
|
||||
|
||||
static const int DXF_max_line = 256;
|
||||
static const int DXF_num_colors = 256;
|
||||
|
||||
/**
|
||||
* A generic DXF-reading class. This class can read a DXF file but doesn't
|
||||
* actually do anything with the data; it's intended to be inherited from and
|
||||
* the appropriate functions overridden (particularly DoneEntity()).
|
||||
*/
|
||||
class DXFFile : public MemoryBase {
|
||||
public:
|
||||
DXFFile();
|
||||
virtual ~DXFFile();
|
||||
|
||||
void process(Filename filename);
|
||||
void process(std::istream *in, bool owns_in);
|
||||
|
||||
// These functions are called as the file is processed. These are the main
|
||||
// hooks for redefining how the class should dispense its data. As each
|
||||
// function is called, the state stored in the DXFFile class reflects the
|
||||
// data that was most recently read.
|
||||
|
||||
virtual void begin_file();
|
||||
virtual void begin_section();
|
||||
virtual void done_vertex();
|
||||
virtual void done_entity();
|
||||
virtual void end_section();
|
||||
virtual void end_file();
|
||||
virtual void error();
|
||||
|
||||
// new_layer() is called whenever the DXFFile class encounters a new Layer
|
||||
// definition, and must allocate a DXFLayer instance. This function is
|
||||
// provided so that user code may force allocate of a specialized DXFLayer
|
||||
// instance instead.
|
||||
virtual DXFLayer *new_layer(const std::string &name) {
|
||||
return new DXFLayer(name);
|
||||
}
|
||||
|
||||
enum State {
|
||||
ST_top,
|
||||
ST_section,
|
||||
ST_entity,
|
||||
ST_verts,
|
||||
ST_error,
|
||||
ST_done,
|
||||
};
|
||||
enum Section {
|
||||
SE_unknown,
|
||||
SE_header,
|
||||
SE_tables,
|
||||
SE_blocks,
|
||||
SE_entities,
|
||||
SE_objects,
|
||||
};
|
||||
enum Entity {
|
||||
EN_unknown,
|
||||
EN_3dface,
|
||||
EN_point,
|
||||
EN_insert,
|
||||
EN_vertex,
|
||||
EN_polyline,
|
||||
};
|
||||
enum PolylineFlags {
|
||||
PF_closed = 0x01,
|
||||
PF_curve_fit = 0x02,
|
||||
PF_spline_fit = 0x04,
|
||||
PF_3d = 0x08,
|
||||
PF_3d_mesh = 0x10,
|
||||
PF_closed_n = 0x20,
|
||||
PF_polyface = 0x40,
|
||||
PF_continuous_linetype = 0x80,
|
||||
};
|
||||
|
||||
// This is a table of standard Autocad colors. DXF files can store only a
|
||||
// limited range of colors; specifically, the 255 colors defined by Autocad.
|
||||
struct Color {
|
||||
double r, g, b;
|
||||
};
|
||||
static Color _colors[DXF_num_colors];
|
||||
|
||||
// find_color() returns the index of the closest matching AutoCAD color to
|
||||
// the indicated r, g, b.
|
||||
static int find_color(double r, double g, double b);
|
||||
|
||||
// get_color() returns the r,g,b of the current entity. It is valid at the
|
||||
// time done_entity() is called.
|
||||
const Color &get_color() const;
|
||||
|
||||
// Some entities are defined in world coordinates, in 3-d space; other
|
||||
// entities are inherently 2-d in nature and are defined in planar
|
||||
// coordinates and must be converted to 3-d space. Call this function from
|
||||
// done_entity() to convert a 2-d entity to 3-d world coordinates.
|
||||
void ocs_2_wcs();
|
||||
|
||||
// These members indicate the current state and describe properties of the
|
||||
// current thing being processed. They are valid at done_entity(), and at
|
||||
// other times.
|
||||
int _flags;
|
||||
Section _section;
|
||||
Entity _entity;
|
||||
LPoint3d _p, _q, _r, _s;
|
||||
LVector3d _z;
|
||||
int _color_index;
|
||||
DXFLayer *_layer;
|
||||
|
||||
// _verts is the list of vertices associated with the current entity. It is
|
||||
// valid at the time done_entity() is called.
|
||||
DXFVertices _verts;
|
||||
|
||||
// This is the set of layers encountered within the DXF file.
|
||||
DXFLayerMap _layers;
|
||||
|
||||
protected:
|
||||
State _state;
|
||||
bool _vertices_follow;
|
||||
LMatrix4d _ocs2wcs;
|
||||
|
||||
std::istream *_in;
|
||||
bool _owns_in;
|
||||
|
||||
int _code;
|
||||
std::string _string;
|
||||
|
||||
void compute_ocs();
|
||||
|
||||
bool get_group();
|
||||
void change_state(State new_state);
|
||||
void change_section(Section new_section);
|
||||
void change_layer(const std::string &layer_name);
|
||||
void change_entity(Entity new_entity);
|
||||
void reset_entity();
|
||||
|
||||
void state_top();
|
||||
void state_section();
|
||||
void state_entity();
|
||||
void state_verts();
|
||||
};
|
||||
|
||||
std::ostream &operator << (std::ostream &out, const DXFFile::State &state);
|
||||
std::ostream &operator << (std::ostream &out, const DXFFile::Section §ion);
|
||||
std::ostream &operator << (std::ostream &out, const DXFFile::Entity &entity);
|
||||
|
||||
#endif
|
||||
@ -1,29 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file dxfLayer.cxx
|
||||
* @author drose
|
||||
* @date 2004-05-04
|
||||
*/
|
||||
|
||||
#include "dxfLayer.h"
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
DXFLayer::
|
||||
DXFLayer(const std::string &name) : Namable(name) {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
DXFLayer::
|
||||
~DXFLayer() {
|
||||
}
|
||||
@ -1,34 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file dxfLayer.h
|
||||
* @author drose
|
||||
* @date 2004-05-04
|
||||
*/
|
||||
|
||||
#ifndef DXFLAYER_H
|
||||
#define DXFLAYER_H
|
||||
|
||||
#include "pandatoolbase.h"
|
||||
#include "namable.h"
|
||||
|
||||
/**
|
||||
* This represents a "layer" as read from the DXF file. A layer may be
|
||||
* defined by reading the header part of the file, or it may be implicitly
|
||||
* defined by an entity's having referenced it.
|
||||
*
|
||||
* User code may derive from DXFLayer to associate private data with each
|
||||
* layer, if desired.
|
||||
*/
|
||||
class DXFLayer : public Namable {
|
||||
public:
|
||||
DXFLayer(const std::string &name);
|
||||
virtual ~DXFLayer();
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,38 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file dxfLayerMap.cxx
|
||||
* @author drose
|
||||
* @date 2004-05-04
|
||||
*/
|
||||
|
||||
#include "dxfLayerMap.h"
|
||||
#include "dxfFile.h"
|
||||
|
||||
/**
|
||||
* Looks up the layer name in the map, and returns a pointer to the associated
|
||||
* DXFLayer. If this is the first time this layer name has been used, creates
|
||||
* a new DXFLayer by the given name. In this case, it calls
|
||||
* dxffile->new_layer() to create the layer, allowing user code to override
|
||||
* this function to create a specialized time, if desired.
|
||||
*/
|
||||
DXFLayer *DXFLayerMap::
|
||||
get_layer(const std::string &name, DXFFile *dxffile) {
|
||||
iterator lmi;
|
||||
lmi = find(name);
|
||||
if (lmi != end()) {
|
||||
// The layer was already here.
|
||||
return (*lmi).second;
|
||||
}
|
||||
|
||||
// Need a new layer.
|
||||
DXFLayer *layer = dxffile->new_layer(name);
|
||||
(*this)[name] = layer;
|
||||
|
||||
return layer;
|
||||
}
|
||||
@ -1,33 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file dxfLayerMap.h
|
||||
* @author drose
|
||||
* @date 2004-05-04
|
||||
*/
|
||||
|
||||
#ifndef DXFLAYERMAP_H
|
||||
#define DXFLAYERMAP_H
|
||||
|
||||
#include "pandatoolbase.h"
|
||||
#include "pmap.h"
|
||||
|
||||
class DXFLayer;
|
||||
class DXFFile;
|
||||
|
||||
/**
|
||||
* A map of string (layer name) to DXFLayer: that is, the layers of a file
|
||||
* ordered by name. This is used as a lookup within DXFFile to locate the
|
||||
* layer associated with a particular entity.
|
||||
*/
|
||||
class DXFLayerMap : public pmap<std::string, DXFLayer *> {
|
||||
public:
|
||||
DXFLayer *get_layer(const std::string &name, DXFFile *dxffile);
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,31 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file dxfVertex.cxx
|
||||
* @author drose
|
||||
* @date 2004-05-04
|
||||
*/
|
||||
|
||||
#include "dxfVertex.h"
|
||||
|
||||
/**
|
||||
* This defines a unique ordering for vertices so that the DXFVertexMap can
|
||||
* group identical vertices together.
|
||||
*/
|
||||
int DXFVertex::
|
||||
operator < (const DXFVertex &other) const {
|
||||
if (fabs(_p[0] - other._p[0]) > 0.0001) {
|
||||
return _p[0] < other._p[0];
|
||||
} else if (fabs(_p[1] - other._p[1]) > 0.0001) {
|
||||
return _p[1] < other._p[1];
|
||||
} else if (fabs(_p[2] - other._p[2]) > 0.0001) {
|
||||
return _p[2] < other._p[2];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -1,38 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file dxfVertex.h
|
||||
* @author drose
|
||||
* @date 2004-05-04
|
||||
*/
|
||||
|
||||
#ifndef DXFVERTEX_H
|
||||
#define DXFVERTEX_H
|
||||
|
||||
#include "pandatoolbase.h"
|
||||
#include "pvector.h"
|
||||
#include "luse.h"
|
||||
|
||||
/**
|
||||
* Stored within DXFFile, this is the basic Vertex data of a DXF file. When
|
||||
* DXFFile::DoneEntity() is called, if the entity is a type to have vertices,
|
||||
* then DXFFile::_verts contains a list of all the vertices that belong to the
|
||||
* entity.
|
||||
*/
|
||||
class DXFVertex {
|
||||
public:
|
||||
DXFVertex() { }
|
||||
DXFVertex(const LPoint3d &p) : _p(p) { }
|
||||
int operator < (const DXFVertex &other) const;
|
||||
|
||||
LPoint3d _p;
|
||||
};
|
||||
|
||||
typedef pvector<DXFVertex> DXFVertices;
|
||||
|
||||
#endif
|
||||
@ -1,4 +0,0 @@
|
||||
#include "dxfFile.cxx"
|
||||
#include "dxfLayer.cxx"
|
||||
#include "dxfLayerMap.cxx"
|
||||
#include "dxfVertex.cxx"
|
||||
@ -1,19 +0,0 @@
|
||||
if(NOT HAVE_EGG)
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(P3DXFEGG_HEADERS
|
||||
dxfToEggConverter.h
|
||||
dxfToEggLayer.h
|
||||
)
|
||||
|
||||
set(P3DXFEGG_SOURCES
|
||||
dxfToEggConverter.cxx
|
||||
dxfToEggLayer.cxx
|
||||
)
|
||||
|
||||
add_library(p3dxfegg STATIC ${P3DXFEGG_HEADERS} ${P3DXFEGG_SOURCES})
|
||||
target_link_libraries(p3dxfegg p3dxf p3eggbase)
|
||||
|
||||
# This is only needed for binaries in the pandatool package. It is not useful
|
||||
# for user applications, so it is not installed.
|
||||
@ -1,145 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file dxfToEggConverter.cxx
|
||||
* @author drose
|
||||
* @date 2004-05-04
|
||||
*/
|
||||
|
||||
#include "dxfToEggConverter.h"
|
||||
#include "dxfToEggLayer.h"
|
||||
#include "eggData.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
DXFToEggConverter::
|
||||
DXFToEggConverter() {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
DXFToEggConverter::
|
||||
DXFToEggConverter(const DXFToEggConverter ©) :
|
||||
SomethingToEggConverter(copy)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
DXFToEggConverter::
|
||||
~DXFToEggConverter() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocates and returns a new copy of the converter.
|
||||
*/
|
||||
SomethingToEggConverter *DXFToEggConverter::
|
||||
make_copy() {
|
||||
return new DXFToEggConverter(*this);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the English name of the file type this converter supports.
|
||||
*/
|
||||
std::string DXFToEggConverter::
|
||||
get_name() const {
|
||||
return "DXF";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the common extension of the file type this converter supports.
|
||||
*/
|
||||
std::string DXFToEggConverter::
|
||||
get_extension() const {
|
||||
return "dxf";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this file type can transparently load compressed files
|
||||
* (with a .pz extension), false otherwise.
|
||||
*/
|
||||
bool DXFToEggConverter::
|
||||
supports_compressed() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the reading of the input file and converting it to egg. Returns
|
||||
* true if successful, false otherwise.
|
||||
*/
|
||||
bool DXFToEggConverter::
|
||||
convert_file(const Filename &filename) {
|
||||
clear_error();
|
||||
|
||||
if (_egg_data->get_coordinate_system() == CS_default) {
|
||||
_egg_data->set_coordinate_system(CS_zup_right);
|
||||
}
|
||||
|
||||
process(filename);
|
||||
return !had_error();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
DXFLayer *DXFToEggConverter::
|
||||
new_layer(const std::string &name) {
|
||||
return new DXFToEggLayer(name, get_egg_data());
|
||||
}
|
||||
|
||||
/**
|
||||
* If the entity is a polygon, creates the corresponding egg polygon.
|
||||
*/
|
||||
void DXFToEggConverter::
|
||||
done_entity() {
|
||||
if (_entity == EN_polyline) {
|
||||
// A Polyline is either an unclosed series of connected line segments, or
|
||||
// a closed polygon of arbitrary complexity.
|
||||
|
||||
if ((_flags & PF_3d) == 0) {
|
||||
// it's a 2-d polygon; convert it to 3-d coordinates.
|
||||
ocs_2_wcs();
|
||||
}
|
||||
|
||||
if (_flags & PF_closed) {
|
||||
// it's closed; create a polygon.
|
||||
nassertv(_layer!=nullptr);
|
||||
((DXFToEggLayer *)_layer)->add_polygon(this);
|
||||
} else {
|
||||
// It's open; create a series of line segments.
|
||||
nassertv(_layer!=nullptr);
|
||||
((DXFToEggLayer *)_layer)->add_line(this);
|
||||
}
|
||||
|
||||
} else if (_entity == EN_3dface) {
|
||||
// DXF can also represent a polygon as a 3DFace. This might be either a
|
||||
// quad or a triangle (if two of the vertices are the same). We'll add
|
||||
// the vertices to our list of vertices and then define the polygon.
|
||||
_verts.clear();
|
||||
_verts.push_back(DXFVertex(_s));
|
||||
_verts.push_back(DXFVertex(_r));
|
||||
_verts.push_back(DXFVertex(_q));
|
||||
_verts.push_back(DXFVertex(_p));
|
||||
|
||||
nassertv(_layer!=nullptr);
|
||||
((DXFToEggLayer *)_layer)->add_polygon(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A hook for user code, if desired. This function is called when some
|
||||
* unexpected error occurs while reading the DXF file.
|
||||
*/
|
||||
void DXFToEggConverter::
|
||||
error() {
|
||||
_error = true;
|
||||
}
|
||||
@ -1,48 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file dxfToEggConverter.h
|
||||
* @author drose
|
||||
* @date 2004-05-04
|
||||
*/
|
||||
|
||||
#ifndef DXFTOEGGCONVERTER_H
|
||||
#define DXFTOEGGCONVERTER_H
|
||||
|
||||
#include "pandatoolbase.h"
|
||||
|
||||
#include "somethingToEggConverter.h"
|
||||
#include "dxfFile.h"
|
||||
|
||||
/**
|
||||
* This class supervises the construction of an EggData structure from a DXF
|
||||
* file.
|
||||
*/
|
||||
class DXFToEggConverter : public SomethingToEggConverter, public DXFFile {
|
||||
public:
|
||||
DXFToEggConverter();
|
||||
DXFToEggConverter(const DXFToEggConverter ©);
|
||||
~DXFToEggConverter();
|
||||
|
||||
virtual SomethingToEggConverter *make_copy();
|
||||
|
||||
virtual std::string get_name() const;
|
||||
virtual std::string get_extension() const;
|
||||
virtual bool supports_compressed() const;
|
||||
|
||||
virtual bool convert_file(const Filename &filename);
|
||||
|
||||
protected:
|
||||
virtual DXFLayer *new_layer(const std::string &name);
|
||||
virtual void done_entity();
|
||||
virtual void error();
|
||||
|
||||
bool _error;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,99 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file dxfToEggLayer.cxx
|
||||
* @author drose
|
||||
* @date 2004-05-04
|
||||
*/
|
||||
|
||||
#include "dxfToEggLayer.h"
|
||||
#include "dxfToEggConverter.h"
|
||||
|
||||
#include "dxfFile.h"
|
||||
#include "eggGroup.h"
|
||||
#include "eggPolygon.h"
|
||||
#include "eggLine.h"
|
||||
#include "eggVertex.h"
|
||||
#include "eggVertexPool.h"
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
DXFToEggLayer::
|
||||
DXFToEggLayer(const std::string &name, EggGroupNode *parent) : DXFLayer(name) {
|
||||
_group = new EggGroup(name);
|
||||
parent->add_child(_group);
|
||||
_vpool = new EggVertexPool(name);
|
||||
_group->add_child(_vpool);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Given that done_entity() has just been called and that the current entity
|
||||
* represents a polygon, adds the corresponding polygon to the layer's
|
||||
* EggGroup and vertex pool.
|
||||
*/
|
||||
void DXFToEggLayer::
|
||||
add_polygon(const DXFToEggConverter *entity) {
|
||||
EggPolygon *poly = new EggPolygon;
|
||||
_group->add_child(poly);
|
||||
|
||||
const DXFFile::Color &color = entity->get_color();
|
||||
poly->set_color(LColor(color.r, color.g, color.b, 1.0));
|
||||
|
||||
// A polyline's vertices are stored in the attached vector by dxf.cxx. They
|
||||
// were defined in the DXF file using a series of "VERTEX" entries.
|
||||
|
||||
// For a 3dface, the vertices are defined explicitly as part of the entity;
|
||||
// but in this case, they were added to the vector before add_polygon() was
|
||||
// called.
|
||||
|
||||
DXFVertices::const_iterator vi;
|
||||
for (vi = entity->_verts.begin();
|
||||
vi != entity->_verts.end();
|
||||
++vi) {
|
||||
poly->add_vertex(add_vertex(*vi));
|
||||
}
|
||||
|
||||
poly->cleanup();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Similar to add_polygon(), but adds a set of point lights instead.
|
||||
*/
|
||||
void DXFToEggLayer::
|
||||
add_line(const DXFToEggConverter *entity) {
|
||||
EggLine *line = new EggLine;
|
||||
_group->add_child(line);
|
||||
|
||||
const DXFFile::Color &color = entity->get_color();
|
||||
line->set_color(LColor(color.r, color.g, color.b, 1.0));
|
||||
|
||||
DXFVertices::const_iterator vi;
|
||||
for (vi = entity->_verts.begin();
|
||||
vi != entity->_verts.end();
|
||||
++vi) {
|
||||
line->add_vertex(add_vertex(*vi));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds a unique vertex to the layer's vertex pool and returns it. If the
|
||||
* vertex was already defined previously, returns the original definition.
|
||||
* This is designed to share the common vertices within a layer.
|
||||
*/
|
||||
EggVertex *DXFToEggLayer::
|
||||
add_vertex(const DXFVertex &vert) {
|
||||
EggVertex egg_vert;
|
||||
egg_vert.set_pos(vert._p);
|
||||
|
||||
return _vpool->create_unique_vertex(egg_vert);
|
||||
}
|
||||
@ -1,48 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file dxfToEggLayer.h
|
||||
* @author drose
|
||||
* @date 2004-05-04
|
||||
*/
|
||||
|
||||
#ifndef DXFTOEGGLAYER_H
|
||||
#define DXFTOEGGLAYER_H
|
||||
|
||||
#include "pandatoolbase.h"
|
||||
|
||||
#include "dxfLayer.h"
|
||||
#include "eggVertexPool.h"
|
||||
#include "eggGroup.h"
|
||||
#include "pointerTo.h"
|
||||
|
||||
class EggGroupNode;
|
||||
class EggVertex;
|
||||
class DXFVertex;
|
||||
class DXFToEggConverter;
|
||||
|
||||
/**
|
||||
* The specialization of DXFLayer used by DXFToEggConverter. It contains a
|
||||
* pointer to an EggGroup and a vertex pool; these are used to build up
|
||||
* polygons grouped by layer in the egg file as each polygon is read from the
|
||||
* DXF file.
|
||||
*/
|
||||
class DXFToEggLayer : public DXFLayer {
|
||||
public:
|
||||
DXFToEggLayer(const std::string &name, EggGroupNode *parent);
|
||||
|
||||
void add_polygon(const DXFToEggConverter *entity);
|
||||
void add_line(const DXFToEggConverter *entity);
|
||||
EggVertex *add_vertex(const DXFVertex &vertex);
|
||||
|
||||
PT(EggVertexPool) _vpool;
|
||||
PT(EggGroup) _group;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
@ -1,19 +0,0 @@
|
||||
if(NOT BUILD_TOOLS)
|
||||
return()
|
||||
endif()
|
||||
|
||||
add_executable(dxf-points dxfPoints.cxx dxfPoints.h)
|
||||
target_link_libraries(dxf-points p3progbase p3dxf)
|
||||
install(TARGETS dxf-points EXPORT Tools COMPONENT Tools DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
|
||||
if(HAVE_EGG)
|
||||
|
||||
add_executable(egg2dxf eggToDXF.cxx eggToDXF.h eggToDXFLayer.cxx eggToDXFLayer.h)
|
||||
target_link_libraries(egg2dxf p3dxfegg p3eggbase p3progbase)
|
||||
install(TARGETS egg2dxf EXPORT Tools COMPONENT Tools DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
|
||||
add_executable(dxf2egg dxfToEgg.cxx dxfToEgg.h)
|
||||
target_link_libraries(dxf2egg p3dxfegg p3eggbase p3progbase)
|
||||
install(TARGETS dxf2egg EXPORT Tools COMPONENT Tools DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
|
||||
endif()
|
||||
@ -1,89 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file dxfPoints.cxx
|
||||
* @author drose
|
||||
* @date 2004-05-04
|
||||
*/
|
||||
|
||||
#include "dxfPoints.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
DXFPoints::
|
||||
DXFPoints() :
|
||||
WithOutputFile(true, true, false)
|
||||
{
|
||||
// Indicate the extension name we expect the user to supply for output
|
||||
// files.
|
||||
_preferred_extension = ".txt";
|
||||
|
||||
set_program_brief("extract points from AutoCAD .dxf files");
|
||||
set_program_description
|
||||
("This program reads an AutoCAD .dxf file and generates a simple "
|
||||
"list of all the points contained within it, one per line, to a "
|
||||
"text file, or to standard output.");
|
||||
|
||||
clear_runlines();
|
||||
add_runline("[opts] input.dxf > output.txt");
|
||||
add_runline("[opts] -o output.txt input.dxf");
|
||||
add_runline("[opts] input.dxf output.txt");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void DXFPoints::
|
||||
run() {
|
||||
// Invoke the DXFFile base class to process the input file.
|
||||
process(_input_filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is inherited from DXFFile, and gets called as each entity (face, line,
|
||||
* whatever) has finished processing.
|
||||
*/
|
||||
void DXFPoints::
|
||||
done_entity() {
|
||||
if (_entity == EN_point) {
|
||||
get_output() << _p << "\n";
|
||||
|
||||
} else if (_entity == EN_insert) {
|
||||
ocs_2_wcs();
|
||||
get_output() << _p << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
bool DXFPoints::
|
||||
handle_args(ProgramBase::Args &args) {
|
||||
if (args.empty()) {
|
||||
nout << "You must specify the .dxf file to read on the command line.\n";
|
||||
return false;
|
||||
|
||||
} else if (args.size() != 1) {
|
||||
nout << "You must specify only one .dxf file to read on the command line.\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
_input_filename = args[0];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
DXFPoints prog;
|
||||
prog.parse_command_line(argc, argv);
|
||||
prog.run();
|
||||
return 0;
|
||||
}
|
||||
@ -1,41 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file dxfPoints.h
|
||||
* @author drose
|
||||
* @date 2004-05-04
|
||||
*/
|
||||
|
||||
#ifndef DXFPOINTS_H
|
||||
#define DXFPOINTS_H
|
||||
|
||||
#include "pandatoolbase.h"
|
||||
#include "programBase.h"
|
||||
#include "withOutputFile.h"
|
||||
|
||||
#include "dxfFile.h"
|
||||
|
||||
/**
|
||||
* A simple program to read a dxf file and list the points contained within it
|
||||
* to a text file.
|
||||
*/
|
||||
class DXFPoints : public ProgramBase, public WithOutputFile, public DXFFile {
|
||||
public:
|
||||
DXFPoints();
|
||||
|
||||
void run();
|
||||
|
||||
virtual void done_entity();
|
||||
|
||||
protected:
|
||||
virtual bool handle_args(Args &args);
|
||||
|
||||
Filename _input_filename;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,74 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file dxfToEgg.cxx
|
||||
* @author drose
|
||||
* @date 2004-05-04
|
||||
*/
|
||||
|
||||
#include "dxfToEgg.h"
|
||||
|
||||
#include "dxfToEggConverter.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
DXFToEgg::
|
||||
DXFToEgg() :
|
||||
SomethingToEgg("DXF", ".dxf")
|
||||
{
|
||||
add_units_options();
|
||||
add_normals_options();
|
||||
add_transform_options();
|
||||
|
||||
set_program_brief("convert AutoCAD .dxf files to .egg files");
|
||||
set_program_description
|
||||
("This program converts DXF (AutoCAD interchange format) to egg. It "
|
||||
"only converts polygon data, with no fancy tricks. DXF does not support "
|
||||
"hierarchical databases, so dxf2egg creates a single group at the root "
|
||||
"level for each layer in the DXF file.");
|
||||
|
||||
redescribe_option
|
||||
("cs",
|
||||
"Specify the coordinate system of the input " + _format_name +
|
||||
" file. Normally, this is z-up.");
|
||||
|
||||
_coordinate_system = CS_zup_right;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void DXFToEgg::
|
||||
run() {
|
||||
nout << "Reading " << _input_filename << "\n";
|
||||
|
||||
_data->set_coordinate_system(_coordinate_system);
|
||||
|
||||
DXFToEggConverter converter;
|
||||
converter.set_egg_data(_data);
|
||||
converter._allow_errors = _allow_errors;
|
||||
|
||||
apply_parameters(converter);
|
||||
|
||||
if (!converter.convert_file(_input_filename)) {
|
||||
nout << "Errors in conversion.\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
write_egg_file();
|
||||
nout << "\n";
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
DXFToEgg prog;
|
||||
prog.parse_command_line(argc, argv);
|
||||
prog.run();
|
||||
return 0;
|
||||
}
|
||||
@ -1,32 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file dxfToEgg.h
|
||||
* @author drose
|
||||
* @date 2004-05-04
|
||||
*/
|
||||
|
||||
#ifndef DXFTOEGG_H
|
||||
#define DXFTOEGG_H
|
||||
|
||||
#include "pandatoolbase.h"
|
||||
|
||||
#include "somethingToEgg.h"
|
||||
#include "dxfToEggConverter.h"
|
||||
|
||||
/**
|
||||
* A program to read a DXF file and generate an egg file.
|
||||
*/
|
||||
class DXFToEgg : public SomethingToEgg {
|
||||
public:
|
||||
DXFToEgg();
|
||||
|
||||
void run();
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,149 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file eggToDXF.cxx
|
||||
* @author drose
|
||||
* @date 2004-05-04
|
||||
*/
|
||||
|
||||
#include "eggToDXF.h"
|
||||
#include "eggPolygon.h"
|
||||
#include "dcast.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
EggToDXF::
|
||||
EggToDXF() :
|
||||
EggToSomething("DXF", ".dxf", true, false)
|
||||
{
|
||||
set_binary_output(true);
|
||||
set_program_brief("convert .egg files to AutoCAD .dxf files");
|
||||
set_program_description
|
||||
("This program converts files from egg format to AutoCAD DXF format. "
|
||||
"Since DXF does not support nested hierarchies, vertex normals, or any "
|
||||
"fancy stuff you are probably used to, there is some information lost "
|
||||
"in the conversion");
|
||||
|
||||
add_option
|
||||
("p", "", 0,
|
||||
"Use POLYLINE to represent polygons instead of the default, 3DFACE.",
|
||||
&EggToDXF::dispatch_none, &_use_polyline);
|
||||
|
||||
_coordinate_system = CS_zup_right;
|
||||
_got_coordinate_system = true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void EggToDXF::
|
||||
run() {
|
||||
get_layers(_data);
|
||||
if (_layers.empty()) {
|
||||
nout << "Egg file contains no polygons. Output file not written.\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// uniquify_names("layer", _layers.begin(), _layers.end());
|
||||
|
||||
std::ostream &out = get_output();
|
||||
|
||||
// Autodesk says we don't need the header, but some DXF-reading programs
|
||||
// might get confused if it's missing. We'll write an empty header.
|
||||
out << "0\nSECTION\n"
|
||||
<< "2\nHEADER\n"
|
||||
<< "0\nENDSEC\n";
|
||||
|
||||
write_tables(out);
|
||||
write_entities(out);
|
||||
out << "0\nEOF\n"; // Mark end of file.
|
||||
|
||||
if (!out) {
|
||||
nout << "An error occurred while writing.\n";
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Traverses the hierarchy, looking for groups that contain polygons. Any
|
||||
* such groups are deemed to be layers, and are added to the layers set.
|
||||
*/
|
||||
void EggToDXF::
|
||||
get_layers(EggGroupNode *group) {
|
||||
bool has_polys = false;
|
||||
|
||||
EggToDXFLayer layer(this, group);
|
||||
|
||||
EggGroupNode::iterator ci;
|
||||
for (ci = group->begin(); ci != group->end(); ++ci) {
|
||||
EggNode *child = (*ci);
|
||||
if (child->is_of_type(EggPolygon::get_class_type())) {
|
||||
EggPolygon *poly = DCAST(EggPolygon, child);
|
||||
has_polys = true;
|
||||
|
||||
layer.add_color(poly->get_color());
|
||||
|
||||
} else if (child->is_of_type(EggGroupNode::get_class_type())) {
|
||||
get_layers(DCAST(EggGroupNode, child));
|
||||
}
|
||||
}
|
||||
|
||||
if (has_polys) {
|
||||
layer.choose_overall_color();
|
||||
_layers.push_back(layer);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Writes out the "layers", e.g. groups. This is just the layers definition
|
||||
* in the tables section at the beginning of the file; the actual geometry
|
||||
* gets written later, in write_entities().
|
||||
*/
|
||||
void EggToDXF::
|
||||
write_tables(std::ostream &out) {
|
||||
out << "0\nSECTION\n"
|
||||
<< "2\nTABLES\n" // Begin TABLES section.
|
||||
<< "0\nTABLE\n"
|
||||
<< "2\nLAYER\n" // Define LAYERS.
|
||||
<< "70\n" << _layers.size() << "\n";
|
||||
|
||||
EggToDXFLayers::iterator li;
|
||||
for (li = _layers.begin(); li != _layers.end(); ++li) {
|
||||
(*li).write_layer(out);
|
||||
}
|
||||
|
||||
out << "0\nENDTAB\n" // End LAYERS definition.
|
||||
<< "0\nENDSEC\n"; // End TABLES section.
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes out the "entities", e.g. polygons, defined for all layers.
|
||||
*/
|
||||
void EggToDXF::
|
||||
write_entities(std::ostream &out) {
|
||||
out << "0\nSECTION\n"
|
||||
<< "2\nENTITIES\n"; // Begin ENTITIES section.
|
||||
|
||||
EggToDXFLayers::iterator li;
|
||||
for (li = _layers.begin(); li != _layers.end(); ++li) {
|
||||
(*li).write_entities(out);
|
||||
}
|
||||
|
||||
out << "0\nENDSEC\n"; // End ENTITIES section.
|
||||
}
|
||||
|
||||
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
EggToDXF prog;
|
||||
prog.parse_command_line(argc, argv);
|
||||
prog.run();
|
||||
return 0;
|
||||
}
|
||||
@ -1,43 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file eggToDXF.h
|
||||
* @author drose
|
||||
* @date 2004-05-04
|
||||
*/
|
||||
|
||||
#ifndef EGGTODXF_H
|
||||
#define EGGTODXF_H
|
||||
|
||||
#include "pandatoolbase.h"
|
||||
|
||||
#include "eggToSomething.h"
|
||||
#include "eggToDXFLayer.h"
|
||||
|
||||
class EggGroupNode;
|
||||
|
||||
/**
|
||||
* A program to read an egg file and write a DXF file.
|
||||
*/
|
||||
class EggToDXF : public EggToSomething {
|
||||
public:
|
||||
EggToDXF();
|
||||
|
||||
void run();
|
||||
|
||||
bool _use_polyline;
|
||||
|
||||
private:
|
||||
void get_layers(EggGroupNode *group);
|
||||
void write_tables(std::ostream &out);
|
||||
void write_entities(std::ostream &out);
|
||||
|
||||
EggToDXFLayers _layers;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,219 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file eggToDXFLayer.cxx
|
||||
* @author drose
|
||||
* @date 2004-05-04
|
||||
*/
|
||||
|
||||
#include "eggToDXFLayer.h"
|
||||
#include "eggToDXF.h"
|
||||
#include "dxfFile.h"
|
||||
#include "eggGroup.h"
|
||||
#include "eggGroupNode.h"
|
||||
#include "eggPolygon.h"
|
||||
#include "dcast.h"
|
||||
|
||||
using std::ostream;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
EggToDXFLayer::
|
||||
EggToDXFLayer(EggToDXF *egg2dxf, EggGroupNode *group) :
|
||||
_egg2dxf(egg2dxf), _group(group)
|
||||
{
|
||||
_layer_color = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
EggToDXFLayer::
|
||||
EggToDXFLayer(const EggToDXFLayer ©) :
|
||||
_egg2dxf(copy._egg2dxf),
|
||||
_group(copy._group),
|
||||
_layer_color(copy._layer_color)
|
||||
{
|
||||
// The copy constructor doesn't bother with the ColorCounts.
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void EggToDXFLayer::
|
||||
operator = (const EggToDXFLayer ©) {
|
||||
_egg2dxf = copy._egg2dxf;
|
||||
_group = copy._group;
|
||||
_layer_color = copy._layer_color;
|
||||
|
||||
// The copy constructor doesn't bother with the ColorCounts.
|
||||
}
|
||||
|
||||
/**
|
||||
* Records that one polygon is defined using the indicated color. This will
|
||||
* get accumulated; the color used by the majority of polygons will become the
|
||||
* layer color.
|
||||
*/
|
||||
void EggToDXFLayer::
|
||||
add_color(const LColor &color) {
|
||||
int autocad_color = get_autocad_color(color);
|
||||
|
||||
ColorCounts::iterator cci;
|
||||
cci = _color_counts.find(autocad_color);
|
||||
if (cci == _color_counts.end()) {
|
||||
// The first time a particular color was used. Count it once.
|
||||
_color_counts[autocad_color] = 1;
|
||||
} else {
|
||||
// This color has been used before. Count it again.
|
||||
(*cci).second++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* After all polygons have been accounted for, chooses the polygon color that
|
||||
* occurred most often as the layer color.
|
||||
*/
|
||||
void EggToDXFLayer::
|
||||
choose_overall_color() {
|
||||
int max_count = 0;
|
||||
|
||||
ColorCounts::iterator cci;
|
||||
for (cci = _color_counts.begin(); cci != _color_counts.end(); ++cci) {
|
||||
int count = (*cci).second;
|
||||
if (count > max_count) {
|
||||
_layer_color = (*cci).first;
|
||||
max_count = count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Writes the layer definition into the table at the beginning of the DXF
|
||||
* file. This does not write the actual geometry; that gets done later by
|
||||
* write_entities().
|
||||
*/
|
||||
void EggToDXFLayer::
|
||||
write_layer(ostream &out) {
|
||||
out << "0\nLAYER\n"
|
||||
<< "2\n" << _group->get_name() << "\n"
|
||||
<< "70\n0\n"
|
||||
<< "62\n" << _layer_color << "\n"
|
||||
<< "6\nCONTINUOUS\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a polygon as a POLYLINE entity.
|
||||
*/
|
||||
void EggToDXFLayer::
|
||||
write_polyline(EggPolygon *poly, ostream &out) {
|
||||
out << "0\nPOLYLINE\n"
|
||||
<< "8\n" << _group->get_name() << "\n"
|
||||
<< "66\n1\n"
|
||||
<< "70\n1\n"
|
||||
<< "62\n" << get_autocad_color(poly->get_color()) << "\n";
|
||||
|
||||
// Since DXF uses a clockwise ordering convention, we must reverse the order
|
||||
// in which we write out the vertices.
|
||||
EggPolygon::reverse_iterator vi;
|
||||
for (vi = poly->rbegin(); vi != poly->rend(); ++vi) {
|
||||
EggVertex *vtx = (*vi);
|
||||
LVecBase3d pos = vtx->get_pos3() * _group->get_vertex_frame();
|
||||
out << "0\nVERTEX\n"
|
||||
<< "10\n" << pos[0] << "\n"
|
||||
<< "20\n" << pos[1] << "\n"
|
||||
<< "30\n" << pos[2] << "\n";
|
||||
}
|
||||
out << "0\nSEQEND\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a polygon as a 3DFACE entity.
|
||||
*/
|
||||
void EggToDXFLayer::
|
||||
write_3d_face(EggPolygon *poly, ostream &out) {
|
||||
if (poly->size() > 4) {
|
||||
// If we have a big polygon, we have to triangulate it, since 3DFaces can
|
||||
// only be tris and quads.
|
||||
PT(EggGroup) group = new EggGroup;
|
||||
poly->triangulate_into(group, true);
|
||||
|
||||
EggGroupNode::iterator ci;
|
||||
for (ci = group->begin(); ci != group->end(); ++ci) {
|
||||
EggNode *child = (*ci);
|
||||
if (child->is_of_type(EggPolygon::get_class_type())) {
|
||||
write_3d_face(DCAST(EggPolygon, child), out);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (poly->size() > 2) {
|
||||
// Otherwise, if we have a tri or a quad, just write it out.
|
||||
out << "0\n3DFACE\n"
|
||||
<< "8\n" << _group->get_name() << "\n";
|
||||
|
||||
// Since DXF uses a clockwise ordering convention, we must reverse the
|
||||
// order in which we write out the vertices.
|
||||
int i;
|
||||
EggPolygon::reverse_iterator vi;
|
||||
for (i = 0, vi = poly->rbegin(); vi != poly->rend(); ++i, ++vi) {
|
||||
EggVertex *vtx = (*vi);
|
||||
LVecBase3d pos = vtx->get_pos3() * _group->get_vertex_frame();
|
||||
out << 10 + i << "\n" << pos[0] << "\n"
|
||||
<< 20 + i << "\n" << pos[1] << "\n"
|
||||
<< 30 + i << "\n" << pos[2] << "\n";
|
||||
if (i == 2 && poly->size() == 3) {
|
||||
// A special case for triangles: repeat the last vertex.
|
||||
out << 11 + i << "\n" << pos[0] << "\n"
|
||||
<< 21 + i << "\n" << pos[1] << "\n"
|
||||
<< 31 + i << "\n" << pos[2] << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Writes out the "entities", e.g. polygons, defined for the current layer.
|
||||
*/
|
||||
void EggToDXFLayer::
|
||||
write_entities(ostream &out) {
|
||||
EggGroupNode::iterator ci;
|
||||
for (ci = _group->begin(); ci != _group->end(); ++ci) {
|
||||
EggNode *child = (*ci);
|
||||
if (child->is_of_type(EggPolygon::get_class_type())) {
|
||||
EggPolygon *poly = DCAST(EggPolygon, child);
|
||||
if (_egg2dxf->_use_polyline) {
|
||||
write_polyline(poly, out);
|
||||
} else {
|
||||
write_3d_face(poly, out);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the AutoCAD color index that most closely matches the indicated
|
||||
* EggColor.
|
||||
*/
|
||||
int EggToDXFLayer::
|
||||
get_autocad_color(const LColor &color) {
|
||||
typedef pmap<LColor, int> ColorMap;
|
||||
static ColorMap _map;
|
||||
|
||||
ColorMap::iterator cmi;
|
||||
cmi = _map.find(color);
|
||||
if (cmi != _map.end()) {
|
||||
return (*cmi).second;
|
||||
}
|
||||
|
||||
int result = DXFFile::find_color(color[0], color[1], color[2]);
|
||||
_map[color] = result;
|
||||
return result;
|
||||
}
|
||||
@ -1,56 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file eggToDXFLayer.h
|
||||
* @author drose
|
||||
* @date 2004-05-04
|
||||
*/
|
||||
|
||||
#ifndef EGGTODXFLAYER_H
|
||||
#define EGGTODXFLAYER_H
|
||||
|
||||
#include "pandatoolbase.h"
|
||||
#include "pmap.h"
|
||||
#include "pvector.h"
|
||||
#include "luse.h"
|
||||
|
||||
class EggToDXF;
|
||||
class EggPolygon;
|
||||
class EggGroupNode;
|
||||
|
||||
/**
|
||||
* A single layer in the DXF file to be written by EggToDXF.
|
||||
*/
|
||||
class EggToDXFLayer {
|
||||
public:
|
||||
EggToDXFLayer(EggToDXF *egg2dxf, EggGroupNode *group);
|
||||
EggToDXFLayer(const EggToDXFLayer ©);
|
||||
void operator = (const EggToDXFLayer ©);
|
||||
|
||||
void add_color(const LColor &color);
|
||||
void choose_overall_color();
|
||||
|
||||
void write_layer(std::ostream &out);
|
||||
void write_polyline(EggPolygon *poly, std::ostream &out);
|
||||
void write_3d_face(EggPolygon *poly, std::ostream &out);
|
||||
void write_entities(std::ostream &out);
|
||||
|
||||
private:
|
||||
int get_autocad_color(const LColor &color);
|
||||
|
||||
typedef pmap<int, int> ColorCounts;
|
||||
ColorCounts _color_counts;
|
||||
|
||||
EggToDXF *_egg2dxf;
|
||||
EggGroupNode *_group;
|
||||
int _layer_color;
|
||||
};
|
||||
|
||||
typedef pvector<EggToDXFLayer> EggToDXFLayers;
|
||||
|
||||
#endif
|
||||
@ -1,25 +0,0 @@
|
||||
if(NOT BUILD_TOOLS)
|
||||
return()
|
||||
endif()
|
||||
|
||||
if(NOT HAVE_EGG OR NOT HAVE_FREETYPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(P3EGG_MKFONT_HEADERS
|
||||
eggMakeFont.h
|
||||
rangeDescription.h rangeDescription.I
|
||||
rangeIterator.h rangeIterator.I
|
||||
)
|
||||
|
||||
set(P3EGG_MKFONT_SOURCES
|
||||
eggMakeFont.cxx
|
||||
rangeDescription.cxx
|
||||
rangeIterator.cxx
|
||||
)
|
||||
|
||||
composite_sources(egg-mkfont P3EGG_MKFONT_SOURCES)
|
||||
add_executable(egg-mkfont ${P3EGG_MKFONT_HEADERS} ${P3EGG_MKFONT_SOURCES})
|
||||
target_link_libraries(egg-mkfont p3palettizer p3eggbase p3pandatoolbase)
|
||||
|
||||
install(TARGETS egg-mkfont EXPORT Tools COMPONENT Tools DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
@ -1,5 +0,0 @@
|
||||
|
||||
#include "eggMakeFont.cxx"
|
||||
#include "rangeDescription.cxx"
|
||||
#include "rangeIterator.cxx"
|
||||
|
||||
@ -1,745 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file eggMakeFont.cxx
|
||||
* @author drose
|
||||
* @date 2001-02-16
|
||||
*/
|
||||
|
||||
#include "eggMakeFont.h"
|
||||
#include "rangeIterator.h"
|
||||
#include "palettizer.h"
|
||||
#include "filenameUnifier.h"
|
||||
#include "eggFile.h"
|
||||
#include "textureImage.h"
|
||||
#include "sourceTextureImage.h"
|
||||
#include "pnmTextMaker.h"
|
||||
#include "pnmTextGlyph.h"
|
||||
#include "eggData.h"
|
||||
#include "eggGroup.h"
|
||||
#include "eggPoint.h"
|
||||
#include "eggPolygon.h"
|
||||
#include "eggTexture.h"
|
||||
#include "eggVertexPool.h"
|
||||
#include "eggVertex.h"
|
||||
#include "string_utils.h"
|
||||
#include "dcast.h"
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
using std::string;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
EggMakeFont::
|
||||
EggMakeFont() : EggWriter(true, false) {
|
||||
set_program_brief("generates .egg files with rasterized font glyphs");
|
||||
set_program_description
|
||||
("egg-mkfont uses the FreeType library to generate an egg file "
|
||||
"and a series of texture images from a font file "
|
||||
"input, such as a TTF file. The resulting egg file "
|
||||
"can be loaded in Panda as a font for rendering text, even "
|
||||
"if FreeType is not compiled into the executing Panda.\n\n"
|
||||
|
||||
"egg-mkfont will normally run the generated egg file through "
|
||||
"egg-palettize automatically as part of the generation process. "
|
||||
"This collects the individual glyph textures into a small number "
|
||||
"of texture maps. If you intend to run the font through egg-palettize "
|
||||
"yourself later, you may choose to omit this step.");
|
||||
|
||||
clear_runlines();
|
||||
add_runline("[opts] -o output.egg font");
|
||||
add_runline("[opts] font output.egg");
|
||||
|
||||
add_option
|
||||
("fg", "r,g,b[,a]", 0,
|
||||
"Specifies the foreground color of the generated texture map. The "
|
||||
"default is white: 1,1,1,1, which leads to the most flexibility "
|
||||
"as the color can be modulated at runtime to any suitable color.",
|
||||
&EggMakeFont::dispatch_color, nullptr, &_fg[0]);
|
||||
|
||||
add_option
|
||||
("bg", "r,g,b[,a]", 0,
|
||||
"Specifies the background color of the generated texture map. The "
|
||||
"default is transparent: 1,1,1,0, which allows the text to be "
|
||||
"visible against any color background by placing a polygon of a "
|
||||
"suitable color behind it. If the alpha component of either -fg "
|
||||
"or -bg is not 1, the generated texture images will include an "
|
||||
"alpha component; if both colors specify an alpha component of 1 "
|
||||
"(or do not specify an alpha compenent), then the generated images "
|
||||
"will not include an alpha component.",
|
||||
&EggMakeFont::dispatch_color, nullptr, &_bg[0]);
|
||||
|
||||
add_option
|
||||
("interior", "r,g,b[,a]", 0,
|
||||
"Specifies the color to render the interior part of a hollow font. "
|
||||
"This is a special effect that involves analysis of the bitmap after "
|
||||
"the font has been rendered, and so is more effective when the pixel "
|
||||
"size is large. It also implies -noaa (but you can use a scale "
|
||||
"factor with -sf to achieve antialiasing).",
|
||||
&EggMakeFont::dispatch_color, &_got_interior, &_interior[0]);
|
||||
|
||||
add_option
|
||||
("chars", "range", 0,
|
||||
"Specifies the characters of the font that are used. The range "
|
||||
"specification may include combinations of decimal or hex unicode "
|
||||
"values (where hex values are identified with a leading 0x), separated "
|
||||
"by commas and hyphens to indicate ranges, e.g. '32-126,0xfa0-0xfff'. "
|
||||
"It also may specify ranges of ASCII characters by enclosing them "
|
||||
"within square brackets, e.g. '[A-Za-z0-9]'. If this is not specified, "
|
||||
"the default set has all ASCII characters and an assorted set of "
|
||||
"latin-1 characters, diacritics and punctuation marks.",
|
||||
&EggMakeFont::dispatch_range, nullptr, &_range);
|
||||
|
||||
add_option
|
||||
("extra", "file.egg", 0,
|
||||
"Specifies additional externally-painted glyphs to mix into the "
|
||||
"generated egg file. The named egg file is expected to contain one "
|
||||
"or more groups, each of which is named with the decimal unicode "
|
||||
"number of a character and should contain one polygon. These groups "
|
||||
"are simply copied into the output egg file as if they were generated "
|
||||
"locally. This option may be repeated.",
|
||||
&EggMakeFont::dispatch_vector_string, nullptr, &_extra_filenames);
|
||||
|
||||
add_option
|
||||
("ppu", "pixels", 0,
|
||||
"Specify the pixels per unit. This is the number of pixels in the "
|
||||
"generated texture map that are used for each onscreen unit (or each "
|
||||
"10 points of font; see -ps). Setting this number larger results in "
|
||||
"an easier-to-read font, but at the cost of more texture memory. "
|
||||
"The default is 40.",
|
||||
&EggMakeFont::dispatch_double, nullptr, &_pixels_per_unit);
|
||||
|
||||
add_option
|
||||
("ps", "size", 0,
|
||||
"Specify the point size of the resulting font. This controls the "
|
||||
"apparent size of the font when it is rendered onscreen. By convention, "
|
||||
"a 10 point font is 1 screen unit high, so the default is 10.",
|
||||
&EggMakeFont::dispatch_double, nullptr, &_point_size);
|
||||
|
||||
add_option
|
||||
("sdf", "", 0,
|
||||
"If this is set, a signed distance field will be generated, which "
|
||||
"results in crisp text even when the text is enlarged or zoomed in.",
|
||||
&EggMakeFont::dispatch_true, nullptr, &_generate_distance_field);
|
||||
|
||||
add_option
|
||||
("pm", "n", 0,
|
||||
"The number of extra pixels around a single character in the "
|
||||
"generated polygon. This may be a floating-point number. The "
|
||||
"default is 1.",
|
||||
&EggMakeFont::dispatch_double, nullptr, &_poly_margin);
|
||||
|
||||
add_option
|
||||
("tm", "n", 0,
|
||||
"The number of extra pixels around each character in the texture map. "
|
||||
"This may only be an integer. The default is 2. This is meaningful "
|
||||
"when -nopal is also used; in the normal case, use -pm to control "
|
||||
"both the polygon size and the texture map spacing.",
|
||||
&EggMakeFont::dispatch_int, nullptr, &_tex_margin);
|
||||
|
||||
add_option
|
||||
("rm", "n", 0,
|
||||
"The amount of padding in screen units to place around the glyph when "
|
||||
"rendered. This differs from -pm in that it has no effect on the "
|
||||
"generated texture map, only on the generated egg. Use this in order to "
|
||||
"space the characters out in case they appear to be too close together "
|
||||
"when rendered. The default is 0.",
|
||||
&EggMakeFont::dispatch_double, nullptr, &_render_margin);
|
||||
|
||||
add_option
|
||||
("sf", "factor", 0,
|
||||
"The scale factor of the generated image. This is the factor by which "
|
||||
"the font image is generated oversized, then reduced to its final size, "
|
||||
"to improve antialiasing. If the specified font contains one "
|
||||
"or more fixed-size fonts instead of a scalable font, the scale factor "
|
||||
"may be automatically adjusted as necessary to scale the closest-"
|
||||
"matching font to the desired pixel size. The default is 2.",
|
||||
&EggMakeFont::dispatch_double, &_got_scale_factor, &_scale_factor);
|
||||
|
||||
add_option
|
||||
("noaa", "", 0,
|
||||
"Disable low-level antialiasing by the Freetype library. "
|
||||
"This is unrelated to the antialiasing that is applied due to the "
|
||||
"scale factor specified by -sf; you may have either one, neither, or "
|
||||
"both kinds of antialiasing enabled.",
|
||||
&EggMakeFont::dispatch_none, &_no_native_aa);
|
||||
|
||||
add_option
|
||||
("nopal", "", 0,
|
||||
"Don't run egg-palettize automatically on the output file, but "
|
||||
"just output the raw egg file and all of its individual texture "
|
||||
"images, one for each glyph.",
|
||||
&EggMakeFont::dispatch_none, &_no_palettize);
|
||||
|
||||
add_option
|
||||
("nr", "", 0,
|
||||
"Don't actually reduce the images after applying the scale factor, but "
|
||||
"leave them at their inflated sizes. Presumably you will reduce "
|
||||
"them later, for instance with egg-palettize.",
|
||||
&EggMakeFont::dispatch_none, &_no_reduce);
|
||||
|
||||
add_option
|
||||
("gp", "pattern", 0,
|
||||
"The pattern to be used to generate the glyph texture images. This "
|
||||
"string will be passed to sprintf to generate the actual file name; it "
|
||||
"should contain the string %d or %x (or some variant such as %03d) "
|
||||
"which will be filled in with the Unicode number of each symbol. "
|
||||
"If it is omitted, the default is based on the name of the egg file. "
|
||||
"This is used only if -nopal is specified; in the normal case, "
|
||||
"without -nopal, use -pp instead.",
|
||||
&EggMakeFont::dispatch_string, nullptr, &_output_glyph_pattern);
|
||||
|
||||
add_option
|
||||
("pp", "pattern", 0,
|
||||
"The pattern to be used to generate the palette texture images. This "
|
||||
"string is effectively passed to egg-palettize as the -tn option, and "
|
||||
"thus should contain %i for the palette index number. This is used "
|
||||
"if -nopal is not specified.",
|
||||
&EggMakeFont::dispatch_string, nullptr, &_output_palette_pattern);
|
||||
|
||||
add_option
|
||||
("palsize", "xsize,ysize", 0,
|
||||
"Specify the size of the palette texture images. This is used if "
|
||||
"-nopal is not specified.",
|
||||
&EggMakeFont::dispatch_int_pair, nullptr, _palette_size);
|
||||
|
||||
add_option
|
||||
("face", "index", 0,
|
||||
"Specify the face index of the particular face within the font file "
|
||||
"to use. Some font files contain multiple faces, indexed beginning "
|
||||
"at 0. The default is face 0.",
|
||||
&EggMakeFont::dispatch_int, nullptr, &_face_index);
|
||||
|
||||
_fg.set(1.0, 1.0, 1.0, 1.0);
|
||||
_bg.set(1.0, 1.0, 1.0, 0.0);
|
||||
_interior.set(1.0, 1.0, 1.0, 1.0);
|
||||
_pixels_per_unit = 40.0;
|
||||
_point_size = 10.0;
|
||||
_poly_margin = 1.0;
|
||||
_tex_margin = 2;
|
||||
_render_margin = 0.0;
|
||||
_palette_size[0] = _palette_size[1] = 512;
|
||||
_face_index = 0;
|
||||
_generate_distance_field = false;
|
||||
|
||||
_text_maker = nullptr;
|
||||
_vpool = nullptr;
|
||||
_group = nullptr;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Does something with the additional arguments on the command line (after all
|
||||
* the -options have been parsed). Returns true if the arguments are good,
|
||||
* false otherwise.
|
||||
*/
|
||||
bool EggMakeFont::
|
||||
handle_args(ProgramBase::Args &args) {
|
||||
if (args.empty()) {
|
||||
nout << "Must specify name of font file on command line.\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
_input_font_filename = args[0];
|
||||
args.pop_front();
|
||||
return EggWriter::handle_args(args);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void EggMakeFont::
|
||||
run() {
|
||||
if (has_output_filename() && !get_output_filename().get_dirname().empty()) {
|
||||
FilenameUnifier::set_rel_dirname(get_output_filename().get_dirname());
|
||||
} else {
|
||||
FilenameUnifier::set_rel_dirname(".");
|
||||
}
|
||||
|
||||
_text_maker = new PNMTextMaker(_input_font_filename, _face_index);
|
||||
if (!_text_maker->is_valid()) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (_got_interior) {
|
||||
_no_native_aa = true;
|
||||
}
|
||||
|
||||
if (!_got_scale_factor) {
|
||||
// The default scale factor is 4 if we are not using FreeType's antialias,
|
||||
// or 2 if we are.
|
||||
if (_generate_distance_field) {
|
||||
_scale_factor = 1.0;
|
||||
} else if (_no_native_aa) {
|
||||
_scale_factor = 4.0;
|
||||
} else {
|
||||
_scale_factor = 2.0;
|
||||
}
|
||||
}
|
||||
|
||||
_text_maker->set_point_size(_point_size);
|
||||
_text_maker->set_native_antialias(!_no_native_aa);
|
||||
_text_maker->set_interior_flag(_got_interior);
|
||||
_text_maker->set_pixels_per_unit(_pixels_per_unit);
|
||||
_text_maker->set_scale_factor(_scale_factor);
|
||||
|
||||
// The text_maker may have had to adjust the pixels per unit and the scale
|
||||
// factor according to what the font supports.
|
||||
_pixels_per_unit = _text_maker->get_pixels_per_unit();
|
||||
_scale_factor = _text_maker->get_scale_factor();
|
||||
|
||||
if (_text_maker->get_font_pixel_size() != 0) {
|
||||
nout << "Using " << _text_maker->get_font_pixel_size() << "-pixel font.\n";
|
||||
}
|
||||
|
||||
// Now we may want to tweak the scale factor so that fonts will actually be
|
||||
// generated big. We have to do this after we have already send the current
|
||||
// _scale_factor through the _text_maker for validation.
|
||||
_palettize_scale_factor = _scale_factor;
|
||||
if (_scale_factor != 1.0 && (_no_reduce || !_no_palettize)) {
|
||||
// If _no_reduce is true (-nr was specified), we want to keep the glyph
|
||||
// textures full-sized, because the user asked for that.
|
||||
|
||||
// If _no_palettize is false (-nopal was not specified), we still want to
|
||||
// keep the glyph textures full-sized, because the palettizer will reduce
|
||||
// them later.
|
||||
|
||||
_tex_margin = (int)(_tex_margin * _scale_factor);
|
||||
_poly_margin *= _scale_factor;
|
||||
_pixels_per_unit *= _scale_factor;
|
||||
_scale_factor = 1.0;
|
||||
_text_maker->set_pixels_per_unit(_pixels_per_unit);
|
||||
_text_maker->set_scale_factor(1.0);
|
||||
}
|
||||
|
||||
if (_no_reduce) {
|
||||
// If -nr was specified, but we're still palettizing, we don't even want
|
||||
// to reduce the palette images. Instead, we'll generate extra-large
|
||||
// palette images.
|
||||
_palette_size[0] = (int)(_palette_size[0] * _palettize_scale_factor);
|
||||
_palette_size[1] = (int)(_palette_size[1] * _palettize_scale_factor);
|
||||
_palettize_scale_factor = 1.0;
|
||||
}
|
||||
|
||||
if (_range.is_empty()) {
|
||||
// If there's no specified range, the default is the entire ASCII set.
|
||||
_range.add_range(0x20, 0x7e);
|
||||
|
||||
_range.add_singleton(0xa1); // Upside down exclamation mark
|
||||
_range.add_singleton(0xa9); // Copyright sign
|
||||
_range.add_singleton(0xab); // Left double angle quote
|
||||
// _range.add_singleton(0xae); Registered sign
|
||||
_range.add_singleton(0xb0); // Degree symbol
|
||||
_range.add_singleton(0xb5); // Mu/micro
|
||||
_range.add_singleton(0xb8); // Cedilla
|
||||
_range.add_singleton(0xbb); // Right double angle quote
|
||||
_range.add_singleton(0xbf); // Upside down question mark
|
||||
|
||||
_range.add_singleton(0xc6); // AE ligature
|
||||
_range.add_singleton(0xc7); // C cedilla
|
||||
// _range.add_singleton(0xd0); Upper-case Eth _range.add_singleton(0xd8);
|
||||
// Upper-case O with line _range.add_singleton(0xde); Upper-case Thorn
|
||||
_range.add_singleton(0xdf); // German Eszet
|
||||
_range.add_singleton(0xe6); // ae ligature
|
||||
_range.add_singleton(0xe7); // c cedilla
|
||||
_range.add_singleton(0xf0); // Lower-case Eth
|
||||
_range.add_singleton(0xf8); // Lower-case O with line
|
||||
_range.add_singleton(0xfe); // Lower-case Thorn
|
||||
|
||||
// _range.add_singleton(0x03c0); pi
|
||||
|
||||
// Dotless i and j, for combining purposes.
|
||||
_range.add_singleton(0x0131);
|
||||
_range.add_singleton(0x0237);
|
||||
|
||||
// And general punctuation. These don't take up much space anyway.
|
||||
_range.add_range(0x2018, 0x201f);
|
||||
|
||||
_range.add_singleton(0x2026); // Ellipses
|
||||
|
||||
// Also add all the combining diacritic marks.
|
||||
_range.add_range(0x0300, 0x030f);
|
||||
}
|
||||
if (_output_glyph_pattern.empty()) {
|
||||
// Create a default texture filename pattern.
|
||||
_output_glyph_pattern = get_output_filename().get_fullpath_wo_extension() + "%03d.png";
|
||||
}
|
||||
if (_output_palette_pattern.empty()) {
|
||||
// Create a default texture filename pattern.
|
||||
_output_palette_pattern = get_output_filename().get_fullpath_wo_extension() + "_%i";
|
||||
}
|
||||
|
||||
// Figure out how many channels we need based on the foreground and
|
||||
// background colors.
|
||||
bool needs_alpha = (_fg[3] != 1.0 || _bg[3] != 1.0 || _interior[3] != 1.0);
|
||||
bool needs_color = (_fg[0] != _fg[1] || _fg[1] != _fg[2] ||
|
||||
_bg[0] != _bg[1] || _bg[1] != _bg[2] ||
|
||||
_interior[0] != _interior[1] || _interior[1] != _interior[2]);
|
||||
|
||||
if (needs_alpha) {
|
||||
if (needs_color) {
|
||||
_num_channels = 4;
|
||||
_format = EggTexture::F_rgba;
|
||||
} else {
|
||||
if (_fg[0] == 1.0 && _bg[0] == 1.0 && _interior[0] == 1.0) {
|
||||
// A special case: we only need an alpha channel. Copy the alpha data
|
||||
// into the color channels so we can write out a one-channel image.
|
||||
_fg[0] = _fg[1] = _fg[2] = _fg[3];
|
||||
_bg[0] = _bg[1] = _bg[2] = _bg[3];
|
||||
_interior[0] = _interior[1] = _interior[2] = _interior[3];
|
||||
_num_channels = 1;
|
||||
_format = EggTexture::F_alpha;
|
||||
} else {
|
||||
_num_channels = 2;
|
||||
_format = EggTexture::F_luminance_alpha;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (needs_color) {
|
||||
_num_channels = 3;
|
||||
_format = EggTexture::F_rgb;
|
||||
} else {
|
||||
_num_channels = 1;
|
||||
_format = EggTexture::F_luminance;
|
||||
}
|
||||
}
|
||||
|
||||
// Create a global Palettizer object. We'll use this even if the user
|
||||
// specified -nopal, if nothing else just to hold all of the TextureImage
|
||||
// pointers.
|
||||
pal = new Palettizer;
|
||||
pal->_generated_image_pattern = _output_palette_pattern;
|
||||
pal->_omit_solitary = false;
|
||||
pal->_round_uvs = false;
|
||||
|
||||
// Generate a txa script for the palettizer. We have the palettizer reduce
|
||||
// all of the texture images by the inverse of our scale factor.
|
||||
char buffer[1024];
|
||||
sprintf(buffer, ":margin 0;:coverage 1000;:background %f %f %f %f;:palette %d %d;*: %f%% keep-format",
|
||||
_bg[0], _bg[1], _bg[2], _bg[3],
|
||||
_palette_size[0], _palette_size[1],
|
||||
100.0 / _palettize_scale_factor);
|
||||
std::istringstream txa_script(buffer);
|
||||
pal->read_txa_file(txa_script, "default script");
|
||||
|
||||
pal->all_params_set();
|
||||
|
||||
// Now create all the egg structures. We can't use _data, since we want to
|
||||
// pass this object to the palettizer, which will try to up its reference
|
||||
// count.
|
||||
PT(EggData) egg_data = new EggData;
|
||||
_group = new EggGroup();
|
||||
egg_data->add_child(_group);
|
||||
append_command_comment(egg_data);
|
||||
|
||||
_vpool = new EggVertexPool("vpool");
|
||||
_group->add_child(_vpool);
|
||||
|
||||
// Make the group a sequence, as a convenience. If we view the egg file
|
||||
// directly we can see all the characters one at a time.
|
||||
_group->set_switch_flag(true);
|
||||
_group->set_switch_fps(2.0);
|
||||
|
||||
double margin = _poly_margin;
|
||||
if (_generate_distance_field) {
|
||||
// Distance fields are always rendered with binary alpha.
|
||||
_group->set_alpha_mode(EggRenderMode::AM_binary);
|
||||
|
||||
// Fudged to make most fonts fit on 512x256.
|
||||
if (_poly_margin >= 1) {
|
||||
margin += 3.5;
|
||||
_poly_margin -= 0.5;
|
||||
}
|
||||
|
||||
_text_maker->set_distance_field_radius(4);
|
||||
}
|
||||
|
||||
// Also create an egg group indicating the font's design size and poly
|
||||
// margin.
|
||||
EggGroup *ds_group = new EggGroup("ds");
|
||||
_group->add_child(ds_group);
|
||||
EggVertex *vtx = make_vertex(LPoint2d(margin / _pixels_per_unit, _text_maker->get_line_height()));
|
||||
EggPoint *point = new EggPoint;
|
||||
ds_group->add_child(point);
|
||||
point->add_vertex(vtx);
|
||||
|
||||
// Finally, add the characters, one at a time.
|
||||
RangeIterator ri(_range);
|
||||
do {
|
||||
add_character(ri.get_code());
|
||||
} while (ri.next());
|
||||
|
||||
// If there are extra glyphs, pick them up.
|
||||
if (!_extra_filenames.empty()) {
|
||||
vector_string::const_iterator si;
|
||||
for (si = _extra_filenames.begin(); si != _extra_filenames.end(); ++si) {
|
||||
add_extra_glyphs(*si);
|
||||
}
|
||||
}
|
||||
|
||||
if (_no_palettize) {
|
||||
// Ok, no palettize step; just write out the egg file and all of the
|
||||
// textures.
|
||||
Textures::iterator ti;
|
||||
for (ti = _textures.begin(); ti != _textures.end(); ++ti) {
|
||||
TextureImage *texture = (*ti);
|
||||
texture->write(texture->read_source_image());
|
||||
}
|
||||
|
||||
egg_data->write_egg(get_output());
|
||||
|
||||
} else {
|
||||
// Pass the generated egg structure through egg-palettize, without writing
|
||||
// it to disk first.
|
||||
string name = get_output_filename().get_basename();
|
||||
EggFile *egg_file = pal->get_egg_file(name);
|
||||
egg_file->from_command_line(egg_data, "", get_output_filename(),
|
||||
get_exec_command());
|
||||
|
||||
pal->add_command_line_egg(egg_file);
|
||||
pal->process_all(true, "");
|
||||
pal->optimal_resize();
|
||||
pal->generate_images(true);
|
||||
if (!pal->write_eggs()) {
|
||||
exit(1);
|
||||
}
|
||||
// pal->report_pi();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
bool EggMakeFont::
|
||||
dispatch_range(const string &, const string &arg, void *var) {
|
||||
RangeDescription *ip = (RangeDescription *)var;
|
||||
return ip->parse_parameter(arg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocates and returns a new vertex from the vertex pool representing the
|
||||
* indicated 2-d coordinates.
|
||||
*/
|
||||
EggVertex *EggMakeFont::
|
||||
make_vertex(const LPoint2d &xy) {
|
||||
return
|
||||
_vpool->make_new_vertex(LPoint3d::origin(_coordinate_system) +
|
||||
LVector3d::rfu(xy[0], 0.0, xy[1], _coordinate_system));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the indicated character and adds it to the font description.
|
||||
*/
|
||||
void EggMakeFont::
|
||||
add_character(int code) {
|
||||
PNMTextGlyph *glyph = _text_maker->get_glyph(code);
|
||||
if (glyph == nullptr) {
|
||||
nout << "No definition in font for character " << code << ".\n";
|
||||
return;
|
||||
}
|
||||
|
||||
make_geom(glyph, code);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates the actual geometry for the glyph.
|
||||
*/
|
||||
void EggMakeFont::
|
||||
make_geom(PNMTextGlyph *glyph, int character) {
|
||||
// Create an egg group to hold the polygon.
|
||||
string group_name = format_string(character);
|
||||
EggGroup *group = new EggGroup(group_name);
|
||||
_group->add_child(group);
|
||||
|
||||
if (glyph->get_width() != 0 && glyph->get_height() != 0) {
|
||||
int bitmap_top = glyph->get_top();
|
||||
int bitmap_left = glyph->get_left();
|
||||
double tex_x_size = glyph->get_width();
|
||||
double tex_y_size = glyph->get_height();
|
||||
|
||||
double poly_margin = _poly_margin;
|
||||
double x_origin = _tex_margin;
|
||||
double y_origin = _tex_margin;
|
||||
double page_y_size = tex_y_size + _tex_margin * 2;
|
||||
double page_x_size = tex_x_size + _tex_margin * 2;
|
||||
|
||||
// Determine the corners of the rectangle in geometric units.
|
||||
double tex_poly_margin = poly_margin / _pixels_per_unit;
|
||||
double origin_y = bitmap_top / _pixels_per_unit;
|
||||
double origin_x = bitmap_left / _pixels_per_unit;
|
||||
double top = origin_y + tex_poly_margin;
|
||||
double left = origin_x - tex_poly_margin;
|
||||
double bottom = origin_y - tex_y_size / _pixels_per_unit - tex_poly_margin;
|
||||
double right = origin_x + tex_x_size / _pixels_per_unit + tex_poly_margin;
|
||||
|
||||
// And the corresponding corners in UV units.
|
||||
double uv_top = 1.0f - (double)(y_origin - poly_margin) / page_y_size;
|
||||
double uv_left = (double)(x_origin - poly_margin) / page_x_size;
|
||||
double uv_bottom = 1.0f - (double)(y_origin + poly_margin + tex_y_size) / page_y_size;
|
||||
double uv_right = (double)(x_origin + poly_margin + tex_x_size) / page_x_size;
|
||||
|
||||
// Create the vertices for the polygon.
|
||||
EggVertex *v1 = make_vertex(LPoint2d(left, bottom));
|
||||
EggVertex *v2 = make_vertex(LPoint2d(right, bottom));
|
||||
EggVertex *v3 = make_vertex(LPoint2d(right, top));
|
||||
EggVertex *v4 = make_vertex(LPoint2d(left, top));
|
||||
|
||||
v1->set_uv(LTexCoordd(uv_left, uv_bottom));
|
||||
v2->set_uv(LTexCoordd(uv_right, uv_bottom));
|
||||
v3->set_uv(LTexCoordd(uv_right, uv_top));
|
||||
v4->set_uv(LTexCoordd(uv_left, uv_top));
|
||||
|
||||
EggPolygon *poly = new EggPolygon();
|
||||
group->add_child(poly);
|
||||
poly->set_texture(get_tref(glyph, character));
|
||||
|
||||
poly->add_vertex(v1);
|
||||
poly->add_vertex(v2);
|
||||
poly->add_vertex(v3);
|
||||
poly->add_vertex(v4);
|
||||
}
|
||||
|
||||
// Now create a single point where the origin of the next character will be.
|
||||
|
||||
EggVertex *v0 = make_vertex(LPoint2d(glyph->get_advance() / _pixels_per_unit + _render_margin, 0.0));
|
||||
EggPoint *point = new EggPoint;
|
||||
group->add_child(point);
|
||||
point->add_vertex(v0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the egg texture reference for a particular glyph, creating it if it
|
||||
* has not already been created.
|
||||
*/
|
||||
EggTexture *EggMakeFont::
|
||||
get_tref(PNMTextGlyph *glyph, int character) {
|
||||
TRefs::iterator ti = _trefs.find(glyph);
|
||||
if (ti != _trefs.end()) {
|
||||
return (*ti).second;
|
||||
}
|
||||
|
||||
EggTexture *tref = make_tref(glyph, character);
|
||||
_trefs[glyph] = tref;
|
||||
return tref;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a texture image for the indicated glyph, and returns its egg
|
||||
* reference.
|
||||
*/
|
||||
EggTexture *EggMakeFont::
|
||||
make_tref(PNMTextGlyph *glyph, int character) {
|
||||
char buffer[1024];
|
||||
sprintf(buffer, _output_glyph_pattern.c_str(), character);
|
||||
|
||||
Filename texture_filename = buffer;
|
||||
PNMImage image(glyph->get_width() + _tex_margin * 2,
|
||||
glyph->get_height() + _tex_margin * 2, _num_channels);
|
||||
image.fill(_bg[0], _bg[1], _bg[2]);
|
||||
if (image.has_alpha()) {
|
||||
image.alpha_fill(_bg[3]);
|
||||
}
|
||||
if (_got_interior) {
|
||||
glyph->place(image, -glyph->get_left() + _tex_margin,
|
||||
glyph->get_top() + _tex_margin, _fg, _interior);
|
||||
} else {
|
||||
glyph->place(image, -glyph->get_left() + _tex_margin,
|
||||
glyph->get_top() + _tex_margin, _fg);
|
||||
}
|
||||
|
||||
// We don't write the image to disk immediately, since it might just get
|
||||
// palettized. But we do record it in a TextureImage object within the
|
||||
// global Palettizer, so that it may be written out later.
|
||||
|
||||
string name = texture_filename.get_basename_wo_extension();
|
||||
TextureImage *texture = pal->get_texture(name);
|
||||
_textures.push_back(texture);
|
||||
texture->set_filename("", texture_filename);
|
||||
SourceTextureImage *source = texture->get_source(texture_filename, "", 0);
|
||||
texture->set_source_image(image);
|
||||
source->set_header(image);
|
||||
|
||||
EggTexture *tref = new EggTexture(name, texture_filename);
|
||||
tref->set_format(_format);
|
||||
tref->set_wrap_mode(EggTexture::WM_clamp);
|
||||
tref->set_minfilter(EggTexture::FT_linear_mipmap_linear);
|
||||
tref->set_magfilter(EggTexture::FT_linear);
|
||||
tref->set_quality_level(EggTexture::QL_best);
|
||||
|
||||
return tref;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the indicated filename and adds any numbered groups into the current
|
||||
* egg file.
|
||||
*/
|
||||
void EggMakeFont::
|
||||
add_extra_glyphs(const Filename &extra_filename) {
|
||||
PT(EggData) extra_data = new EggData;
|
||||
|
||||
if (!extra_data->read(extra_filename)) {
|
||||
return;
|
||||
}
|
||||
|
||||
_group->steal_children(*extra_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively searches for numbered groups in the indicated egg file, and
|
||||
* copies them to the current egg file.
|
||||
*/
|
||||
void EggMakeFont::
|
||||
r_add_extra_glyphs(EggGroupNode *egg_group) {
|
||||
if (egg_group->is_of_type(EggGroup::get_class_type())) {
|
||||
EggGroup *group = DCAST(EggGroup, egg_group);
|
||||
if (is_numeric(group->get_name())) {
|
||||
EggGroup *new_group = new EggGroup(group->get_name());
|
||||
_group->add_child(new_group);
|
||||
new_group->steal_children(*group);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
EggGroupNode::iterator ci;
|
||||
for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
|
||||
EggNode *child = (*ci);
|
||||
if (child->is_of_type(EggGroupNode::get_class_type())) {
|
||||
r_add_extra_glyphs(DCAST(EggGroupNode, child));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the indicated string is all numeric digits, false
|
||||
* otherwise.
|
||||
*/
|
||||
bool EggMakeFont::
|
||||
is_numeric(const string &str) {
|
||||
if (str.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string::const_iterator si;
|
||||
for (si = str.begin(); si != str.end(); ++si) {
|
||||
if (!isdigit(*si)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
EggMakeFont prog;
|
||||
prog.parse_command_line(argc, argv);
|
||||
prog.run();
|
||||
return 0;
|
||||
}
|
||||
@ -1,100 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file eggMakeFont.h
|
||||
* @author drose
|
||||
* @date 2001-02-16
|
||||
*/
|
||||
|
||||
#ifndef EGGMAKEFONT_H
|
||||
#define EGGMAKEFONT_H
|
||||
|
||||
#include "pandatoolbase.h"
|
||||
#include "rangeDescription.h"
|
||||
|
||||
#include "eggWriter.h"
|
||||
#include "eggTexture.h"
|
||||
#include "pmap.h"
|
||||
#include "pvector.h"
|
||||
#include "vector_string.h"
|
||||
|
||||
class PNMTextMaker;
|
||||
class PNMTextGlyph;
|
||||
class EggVertexPool;
|
||||
class EggGroup;
|
||||
class TextureImage;
|
||||
|
||||
/**
|
||||
* This program uses FreeType to generate an egg file and a series of texture
|
||||
* images from a font file input, such as a TTF file. The resulting egg file
|
||||
* can be loaded in Panda as a StaticTextFont object for rendering text, even
|
||||
* if FreeType is not compiled into the executing Panda.
|
||||
*/
|
||||
class EggMakeFont : public EggWriter {
|
||||
public:
|
||||
EggMakeFont();
|
||||
|
||||
protected:
|
||||
virtual bool handle_args(Args &args);
|
||||
|
||||
public:
|
||||
void run();
|
||||
|
||||
private:
|
||||
static bool dispatch_range(const std::string &, const std::string &arg, void *var);
|
||||
EggVertex *make_vertex(const LPoint2d &xy);
|
||||
|
||||
void add_character(int code);
|
||||
void make_geom(PNMTextGlyph *glyph, int character);
|
||||
EggTexture *get_tref(PNMTextGlyph *glyph, int character);
|
||||
EggTexture *make_tref(PNMTextGlyph *glyph, int character);
|
||||
void add_extra_glyphs(const Filename &extra_filename);
|
||||
void r_add_extra_glyphs(EggGroupNode *egg_group);
|
||||
static bool is_numeric(const std::string &str);
|
||||
|
||||
|
||||
private:
|
||||
LColor _fg, _bg, _interior;
|
||||
bool _got_interior;
|
||||
RangeDescription _range;
|
||||
vector_string _extra_filenames;
|
||||
double _pixels_per_unit;
|
||||
double _point_size;
|
||||
double _poly_margin;
|
||||
int _tex_margin;
|
||||
double _render_margin;
|
||||
bool _got_scale_factor;
|
||||
double _scale_factor;
|
||||
bool _no_reduce;
|
||||
bool _no_native_aa;
|
||||
bool _no_palettize;
|
||||
int _palette_size[2];
|
||||
bool _generate_distance_field;
|
||||
|
||||
double _palettize_scale_factor;
|
||||
Filename _input_font_filename;
|
||||
int _face_index;
|
||||
std::string _output_glyph_pattern;
|
||||
std::string _output_palette_pattern;
|
||||
|
||||
PNMTextMaker *_text_maker;
|
||||
|
||||
EggTexture::Format _format;
|
||||
int _num_channels;
|
||||
EggVertexPool *_vpool;
|
||||
EggGroup *_group;
|
||||
|
||||
typedef pmap<PNMTextGlyph *, EggTexture *> TRefs;
|
||||
TRefs _trefs;
|
||||
|
||||
typedef pvector<TextureImage *> Textures;
|
||||
Textures _textures;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
@ -1,61 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file rangeDescription.I
|
||||
* @author drose
|
||||
* @date 2003-09-07
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
INLINE void RangeDescription::
|
||||
add_singleton(int code) {
|
||||
_range_list.push_back(Range(code));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
INLINE void RangeDescription::
|
||||
add_range(int from_code, int to_code) {
|
||||
_range_list.push_back(Range(from_code, to_code));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if there are no codes described in the range.
|
||||
*/
|
||||
INLINE bool RangeDescription::
|
||||
is_empty() const {
|
||||
return _range_list.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
INLINE RangeDescription::Range::
|
||||
Range(int code) :
|
||||
_from_code(code),
|
||||
_to_code(code)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
INLINE RangeDescription::Range::
|
||||
Range(int from_code, int to_code) :
|
||||
_from_code(from_code),
|
||||
_to_code(to_code)
|
||||
{
|
||||
}
|
||||
|
||||
INLINE std::ostream &operator << (std::ostream &out, const RangeDescription &range) {
|
||||
range.output(out);
|
||||
return out;
|
||||
}
|
||||
@ -1,175 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file rangeDescription.cxx
|
||||
* @author drose
|
||||
* @date 2003-09-07
|
||||
*/
|
||||
|
||||
#include "rangeDescription.h"
|
||||
#include "string_utils.h"
|
||||
#include "pnotify.h"
|
||||
|
||||
using std::string;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
RangeDescription::
|
||||
RangeDescription() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a string of comma- and hyphen-delimited unicode values, in decimal
|
||||
* and/or hex, including possible bracket-delimited ASCII characters, as may
|
||||
* have been passed on a command line. Returns true if the parameter is
|
||||
* parsed correctly, false otherwise.
|
||||
*/
|
||||
bool RangeDescription::
|
||||
parse_parameter(const string ¶m) {
|
||||
// First, go through and separate the string by commas. We have to do this
|
||||
// by hand instead of calling tokenize(), because we also have to scan for
|
||||
// square brackets, which may contain nested commas.
|
||||
size_t p = 0;
|
||||
while (p < param.length()) {
|
||||
size_t q = param.find_first_of("[,", p);
|
||||
if (q == string::npos) {
|
||||
return parse_word(trim(param.substr(p)));
|
||||
}
|
||||
if (!parse_word(trim(param.substr(p, q - p)))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (param[q] == '[') {
|
||||
// A square bracket means we must search for the matching square
|
||||
// bracket. However, a right bracket immediately after the left bracket
|
||||
// doesn't count; we start the scan after that.
|
||||
p = param.find("]", q + 2);
|
||||
if ( p == string::npos) {
|
||||
nout << "Unclosed open bracket.\n";
|
||||
return false;
|
||||
}
|
||||
if (!parse_bracket(param.substr(q + 1, p - q - 1))) {
|
||||
return false;
|
||||
}
|
||||
p = p + 1;
|
||||
|
||||
} else {
|
||||
// Otherwise, if the separator was just a comma, the next character
|
||||
// begins the next word.
|
||||
p = q + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void RangeDescription::
|
||||
output(std::ostream &out) const {
|
||||
bool first_time = true;
|
||||
RangeList::const_iterator ri;
|
||||
for (ri = _range_list.begin(); ri != _range_list.end(); ++ri) {
|
||||
const Range &range = (*ri);
|
||||
if (!first_time) {
|
||||
out << ",";
|
||||
}
|
||||
first_time = false;
|
||||
if (range._from_code == range._to_code) {
|
||||
out << range._from_code;
|
||||
} else {
|
||||
out << range._from_code << "-" << range._to_code;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a single "word", i.e. the text delimited by commas, that might be
|
||||
* listed on the command line. This is generally either the empty string, a
|
||||
* single number, or a pair of numbers separated by a hyphen.
|
||||
*/
|
||||
bool RangeDescription::
|
||||
parse_word(const string &word) {
|
||||
if (word.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// It's not empty, so see if it includes a hyphen.
|
||||
size_t hyphen = word.find('-');
|
||||
if (hyphen == string::npos) {
|
||||
// Nope, just one number.
|
||||
int code;
|
||||
if (!parse_code(word, code)) {
|
||||
return false;
|
||||
}
|
||||
add_singleton(code);
|
||||
|
||||
} else {
|
||||
// Two numbers separated by a hyphen.
|
||||
int from_code, to_code;
|
||||
if (!parse_code(word.substr(0, hyphen), from_code)) {
|
||||
return false;
|
||||
}
|
||||
if (!parse_code(word.substr(hyphen + 1), to_code)) {
|
||||
return false;
|
||||
}
|
||||
add_range(from_code, to_code);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a single numeric value, either decimal or hexadecimal, and stores it
|
||||
* in the indicated parameter. Returns true if successful, false otherwise.
|
||||
*/
|
||||
bool RangeDescription::
|
||||
parse_code(const string &word, int &code) {
|
||||
string str = trim(word);
|
||||
const char *nptr = str.c_str();
|
||||
char *endptr;
|
||||
code = strtol(nptr, &endptr, 0);
|
||||
if (*endptr == '\0') {
|
||||
return true;
|
||||
}
|
||||
|
||||
nout << "Invalid Unicode value: " << word << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the text listed between square brackets on the command line.
|
||||
*/
|
||||
bool RangeDescription::
|
||||
parse_bracket(const string &str) {
|
||||
string::const_iterator si;
|
||||
si = str.begin();
|
||||
while (si != str.end()) {
|
||||
int ch = (*si);
|
||||
++si;
|
||||
if (si != str.end() && (*si) == '-') {
|
||||
// A hyphen indicates a range.
|
||||
++si;
|
||||
if (si == str.end()) {
|
||||
// Unless the hyphen is the last character.
|
||||
add_singleton(ch);
|
||||
add_singleton('-');
|
||||
} else {
|
||||
add_range(ch, (*si));
|
||||
++si;
|
||||
}
|
||||
} else {
|
||||
// Anything other than a hyphen indicates a singleton.
|
||||
add_singleton(ch);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -1,60 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file rangeDescription.h
|
||||
* @author drose
|
||||
* @date 2003-09-07
|
||||
*/
|
||||
|
||||
#ifndef RANGEDESCRIPTION_H
|
||||
#define RANGEDESCRIPTION_H
|
||||
|
||||
#include "pandatoolbase.h"
|
||||
#include "pvector.h"
|
||||
|
||||
/**
|
||||
* This describes a sparse range of Unicode character codes for conversion
|
||||
* that may be specified on the command line.
|
||||
*/
|
||||
class RangeDescription {
|
||||
public:
|
||||
RangeDescription();
|
||||
|
||||
bool parse_parameter(const std::string ¶m);
|
||||
INLINE void add_singleton(int code);
|
||||
INLINE void add_range(int from_code, int to_code);
|
||||
INLINE bool is_empty() const;
|
||||
|
||||
void output(std::ostream &out) const;
|
||||
|
||||
private:
|
||||
bool parse_word(const std::string &word);
|
||||
bool parse_code(const std::string &word, int &code);
|
||||
bool parse_bracket(const std::string &str);
|
||||
|
||||
private:
|
||||
class Range {
|
||||
public:
|
||||
INLINE Range(int code);
|
||||
INLINE Range(int from_code, int to_code);
|
||||
|
||||
int _from_code;
|
||||
int _to_code;
|
||||
};
|
||||
|
||||
typedef pvector<Range> RangeList;
|
||||
RangeList _range_list;
|
||||
|
||||
friend class RangeIterator;
|
||||
};
|
||||
|
||||
INLINE std::ostream &operator << (std::ostream &out, const RangeDescription &range);
|
||||
|
||||
#include "rangeDescription.I"
|
||||
|
||||
#endif
|
||||
@ -1,29 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file rangeIterator.I
|
||||
* @author drose
|
||||
* @date 2003-09-07
|
||||
*/
|
||||
|
||||
/**
|
||||
* Returns the current Unicode value represented by the iterator, or -1 if the
|
||||
* iterator has reached the end.
|
||||
*/
|
||||
INLINE int RangeIterator::
|
||||
get_code() const {
|
||||
return _code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if all the code have been retrieved, false otherwise.
|
||||
*/
|
||||
INLINE bool RangeIterator::
|
||||
eof() const {
|
||||
return (_it == _desc._range_list.end());
|
||||
}
|
||||
@ -1,63 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file rangeIterator.cxx
|
||||
* @author drose
|
||||
* @date 2003-09-07
|
||||
*/
|
||||
|
||||
#include "rangeIterator.h"
|
||||
|
||||
/**
|
||||
* Constructs an iterator to walk through the codes on the descriptor. It is
|
||||
* important not to modify the RangeDescription object during the lifetime of
|
||||
* the iterator.
|
||||
*/
|
||||
RangeIterator::
|
||||
RangeIterator(const RangeDescription &desc) :
|
||||
_desc(desc)
|
||||
{
|
||||
_it = _desc._range_list.begin();
|
||||
if (_it == _desc._range_list.end()) {
|
||||
_code = -1;
|
||||
} else {
|
||||
_code = (*_it)._from_code;
|
||||
_codes_generated.insert(_code);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Advances the iterator to the next code. Returns true if there is a next
|
||||
* code, or false if there are no mode codes.
|
||||
*/
|
||||
bool RangeIterator::
|
||||
next() {
|
||||
do {
|
||||
if (_it == _desc._range_list.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_code < (*_it)._to_code) {
|
||||
_code++;
|
||||
|
||||
} else {
|
||||
_it++;
|
||||
if (_it == _desc._range_list.end()) {
|
||||
_code = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
_code = (*_it)._from_code;
|
||||
}
|
||||
|
||||
// If this code has already been generated, repeat and skip to the next
|
||||
// one.
|
||||
} while (!_codes_generated.insert(_code).second);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -1,45 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file rangeIterator.h
|
||||
* @author drose
|
||||
* @date 2003-09-07
|
||||
*/
|
||||
|
||||
#ifndef RANGEITERATOR_H
|
||||
#define RANGEITERATOR_H
|
||||
|
||||
#include "pandatoolbase.h"
|
||||
#include "rangeDescription.h"
|
||||
|
||||
#include "pset.h"
|
||||
|
||||
/**
|
||||
* Walks through all the Unicode characters described by a RangeDescription
|
||||
* class.
|
||||
*/
|
||||
class RangeIterator {
|
||||
public:
|
||||
RangeIterator(const RangeDescription &desc);
|
||||
|
||||
INLINE int get_code() const;
|
||||
bool next();
|
||||
INLINE bool eof() const;
|
||||
|
||||
private:
|
||||
const RangeDescription &_desc;
|
||||
RangeDescription::RangeList::const_iterator _it;
|
||||
int _code;
|
||||
|
||||
typedef pset<int> Codes;
|
||||
Codes _codes_generated;
|
||||
};
|
||||
|
||||
#include "rangeIterator.I"
|
||||
|
||||
#endif
|
||||
@ -1,27 +0,0 @@
|
||||
if(NOT BUILD_TOOLS)
|
||||
return()
|
||||
endif()
|
||||
|
||||
if(NOT HAVE_EGG)
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(P3EGG_OPTCHAR_HEADERS
|
||||
config_egg_optchar.h
|
||||
eggOptchar.h
|
||||
eggOptcharUserData.h eggOptcharUserData.I
|
||||
vertexMembership.h vertexMembership.I
|
||||
)
|
||||
|
||||
set(P3EGG_OPTCHAR_SOURCES
|
||||
config_egg_optchar.cxx
|
||||
eggOptchar.cxx
|
||||
eggOptcharUserData.cxx
|
||||
vertexMembership.cxx
|
||||
)
|
||||
|
||||
composite_sources(egg-optchar P3EGG_OPTCHAR_SOURCES)
|
||||
add_executable(egg-optchar ${P3EGG_OPTCHAR_HEADERS} ${P3EGG_OPTCHAR_SOURCES})
|
||||
target_link_libraries(egg-optchar p3eggcharbase)
|
||||
|
||||
install(TARGETS egg-optchar EXPORT Tools COMPONENT Tools DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
@ -1,41 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file config_egg_optchar.cxx
|
||||
* @author drose
|
||||
* @date 2003-07-18
|
||||
*/
|
||||
|
||||
#include "config_egg_optchar.h"
|
||||
#include "eggOptcharUserData.h"
|
||||
|
||||
#include "dconfig.h"
|
||||
|
||||
|
||||
Configure(config_egg_optchar);
|
||||
|
||||
ConfigureFn(config_egg_optchar) {
|
||||
init_egg_optchar();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the library. This must be called at least once before any of
|
||||
* the functions or classes in this library can be used. Normally it will be
|
||||
* called by the static initializers and need not be called explicitly, but
|
||||
* special cases exist.
|
||||
*/
|
||||
void
|
||||
init_egg_optchar() {
|
||||
static bool initialized = false;
|
||||
if (initialized) {
|
||||
return;
|
||||
}
|
||||
initialized = true;
|
||||
|
||||
EggOptcharUserData::init_type();
|
||||
}
|
||||
@ -1,21 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file config_egg_optchar.h
|
||||
* @author drose
|
||||
* @date 2003-07-18
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_EGG_OPTCHAR_H
|
||||
#define CONFIG_EGG_OPTCHAR_H
|
||||
|
||||
#include "pandatoolbase.h"
|
||||
|
||||
void init_egg_optchar();
|
||||
|
||||
#endif /* CONFIG_EGG_OPTCHAR_H */
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,129 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file eggOptchar.h
|
||||
* @author drose
|
||||
* @date 2003-07-18
|
||||
*/
|
||||
|
||||
#ifndef EGGOPTCHAR_H
|
||||
#define EGGOPTCHAR_H
|
||||
|
||||
#include "pandatoolbase.h"
|
||||
|
||||
#include "eggCharacterFilter.h"
|
||||
#include "luse.h"
|
||||
|
||||
#include "pvector.h"
|
||||
#include "vector_string.h"
|
||||
#include "globPattern.h"
|
||||
|
||||
class EggCharacterData;
|
||||
class EggComponentData;
|
||||
class EggJointData;
|
||||
class EggSliderData;
|
||||
class EggGroupNode;
|
||||
|
||||
/**
|
||||
* Performs basic optimizations of a character model and its associated
|
||||
* animations, by analyzing the animation tables and removing unneeded joints
|
||||
* and/or morphs. Can also be used to restructure the character hierarchy.
|
||||
*/
|
||||
class EggOptchar : public EggCharacterFilter {
|
||||
public:
|
||||
EggOptchar();
|
||||
|
||||
void run();
|
||||
|
||||
protected:
|
||||
virtual bool handle_args(Args &args);
|
||||
|
||||
private:
|
||||
static bool dispatch_vector_string_pair(const std::string &opt, const std::string &arg, void *var);
|
||||
static bool dispatch_name_components(const std::string &opt, const std::string &arg, void *var);
|
||||
static bool dispatch_double_components(const std::string &opt, const std::string &arg, void *var);
|
||||
static bool dispatch_flag_groups(const std::string &opt, const std::string &arg, void *var);
|
||||
|
||||
void determine_removed_components();
|
||||
void move_vertices();
|
||||
bool process_joints();
|
||||
EggJointData *find_best_parent(EggJointData *joint_data) const;
|
||||
EggJointData *find_best_vertex_joint(EggJointData *joint_data) const;
|
||||
|
||||
bool apply_user_reparents();
|
||||
bool zero_channels();
|
||||
bool quantize_channels();
|
||||
void analyze_joints(EggJointData *joint_data, int level);
|
||||
void analyze_sliders(EggCharacterData *char_data);
|
||||
void list_joints(EggJointData *joint_data, int indent_level, bool verbose);
|
||||
void list_joints_p(EggJointData *joint_data, int &col);
|
||||
void list_scalars(EggCharacterData *char_data, bool verbose);
|
||||
void describe_component(EggComponentData *comp_data, int indent_level,
|
||||
bool verbose);
|
||||
void do_reparent();
|
||||
|
||||
void quantize_vertices();
|
||||
void quantize_vertices(EggNode *egg_node);
|
||||
void quantize_vertex(EggVertex *egg_vertex);
|
||||
|
||||
void do_flag_groups(EggGroupNode *egg_group);
|
||||
void rename_joints();
|
||||
void rename_primitives(EggGroupNode *egg_group, const std::string &name);
|
||||
void change_dart_type(EggGroupNode *egg_group, const std::string &new_dart_type);
|
||||
void do_preload();
|
||||
void do_defpose();
|
||||
|
||||
bool _list_hierarchy;
|
||||
bool _list_hierarchy_v;
|
||||
bool _list_hierarchy_p;
|
||||
bool _preload;
|
||||
bool _keep_all;
|
||||
|
||||
class StringPair {
|
||||
public:
|
||||
std::string _a;
|
||||
std::string _b;
|
||||
};
|
||||
typedef pvector<StringPair> StringPairs;
|
||||
StringPairs _new_joints;
|
||||
StringPairs _reparent_joints;
|
||||
StringPairs _zero_channels;
|
||||
StringPairs _rename_joints;
|
||||
|
||||
vector_string _keep_components;
|
||||
vector_string _drop_components;
|
||||
vector_string _expose_components;
|
||||
vector_string _suppress_components;
|
||||
|
||||
std::string _dart_type;
|
||||
|
||||
class DoubleString {
|
||||
public:
|
||||
double _a;
|
||||
std::string _b;
|
||||
};
|
||||
typedef pvector<DoubleString> DoubleStrings;
|
||||
DoubleStrings _quantize_anims;
|
||||
|
||||
typedef pvector<GlobPattern> Globs;
|
||||
|
||||
class FlagGroupsEntry {
|
||||
public:
|
||||
Globs _groups;
|
||||
std::string _name;
|
||||
};
|
||||
typedef pvector<FlagGroupsEntry> FlagGroups;
|
||||
FlagGroups _flag_groups;
|
||||
|
||||
std::string _defpose;
|
||||
|
||||
bool _optimal_hierarchy;
|
||||
double _vref_quantum;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,79 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file eggOptcharUserData.I
|
||||
* @author drose
|
||||
* @date 2003-07-18
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
INLINE EggOptcharUserData::
|
||||
EggOptcharUserData() {
|
||||
_flags = 0;
|
||||
_static_mat = LMatrix4d::ident_mat();
|
||||
_static_value = 0.0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
INLINE EggOptcharUserData::
|
||||
EggOptcharUserData(const EggOptcharUserData ©) :
|
||||
EggUserData(copy),
|
||||
_flags(copy._flags),
|
||||
_static_mat(copy._static_mat),
|
||||
_static_value(copy._static_value)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
INLINE void EggOptcharUserData::
|
||||
operator = (const EggOptcharUserData ©) {
|
||||
EggUserData::operator = (copy);
|
||||
_flags = copy._flags;
|
||||
_static_mat = copy._static_mat;
|
||||
_static_value = copy._static_value;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
INLINE bool EggOptcharUserData::
|
||||
is_static() const {
|
||||
return (_flags & F_static) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
INLINE bool EggOptcharUserData::
|
||||
is_identity() const {
|
||||
return (_flags & F_identity) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
INLINE bool EggOptcharUserData::
|
||||
is_empty() const {
|
||||
return (_flags & F_empty) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
INLINE bool EggOptcharUserData::
|
||||
is_top() const {
|
||||
return (_flags & F_top) != 0;
|
||||
}
|
||||
@ -1,16 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file eggOptcharUserData.cxx
|
||||
* @author drose
|
||||
* @date 2003-07-18
|
||||
*/
|
||||
|
||||
#include "eggOptcharUserData.h"
|
||||
|
||||
TypeHandle EggOptcharUserData::_type_handle;
|
||||
@ -1,69 +0,0 @@
|
||||
/**
|
||||
* PANDA 3D SOFTWARE
|
||||
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
||||
*
|
||||
* All use of this software is subject to the terms of the revised BSD
|
||||
* license. You should have received a copy of this license along
|
||||
* with this source code in a file named "LICENSE."
|
||||
*
|
||||
* @file eggOptcharUserData.h
|
||||
* @author drose
|
||||
* @date 2003-07-18
|
||||
*/
|
||||
|
||||
#ifndef EGGOPTCHARUSERDATA_H
|
||||
#define EGGOPTCHARUSERDATA_H
|
||||
|
||||
#include "pandatoolbase.h"
|
||||
#include "eggUserData.h"
|
||||
#include "luse.h"
|
||||
|
||||
/**
|
||||
* This class contains extra user data which is piggybacked onto EggGroup
|
||||
* objects for the purpose of the maya converter.
|
||||
*/
|
||||
class EggOptcharUserData : public EggUserData {
|
||||
public:
|
||||
INLINE EggOptcharUserData();
|
||||
INLINE EggOptcharUserData(const EggOptcharUserData ©);
|
||||
INLINE void operator = (const EggOptcharUserData ©);
|
||||
|
||||
INLINE bool is_static() const;
|
||||
INLINE bool is_identity() const;
|
||||
INLINE bool is_empty() const;
|
||||
INLINE bool is_top() const;
|
||||
|
||||
enum Flags {
|
||||
F_static = 0x0001,
|
||||
F_identity = 0x0002,
|
||||
F_empty = 0x0004,
|
||||
F_top = 0x0008,
|
||||
F_remove = 0x0010,
|
||||
F_expose = 0x0020,
|
||||
F_suppress = 0x0040,
|
||||
};
|
||||
int _flags;
|
||||
LMatrix4d _static_mat;
|
||||
double _static_value;
|
||||
|
||||
public:
|
||||
static TypeHandle get_class_type() {
|
||||
return _type_handle;
|
||||
}
|
||||
static void init_type() {
|
||||
EggUserData::init_type();
|
||||
register_type(_type_handle, "EggOptcharUserData",
|
||||
EggUserData::get_class_type());
|
||||
}
|
||||
virtual TypeHandle get_type() const {
|
||||
return get_class_type();
|
||||
}
|
||||
virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
|
||||
|
||||
private:
|
||||
static TypeHandle _type_handle;
|
||||
};
|
||||
|
||||
#include "eggOptcharUserData.I"
|
||||
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user