layer1/COLLADA.cpp (1,304 lines of code) (raw):
/*
* PyMOL COLLADA export
*
* (c) 2014 Jared Sampson
* (c) 2014 Schrodinger, Inc.
*/
#include "os_predef.h"
#include "os_std.h"
#include "Feedback.h"
#include "Ray.h"
#ifdef _HAVE_LIBXML
#include <libxml/xmlwriter.h>
#include <libxml/encoding.h>
#include <time.h>
#include "Setting.h"
#include "Ortho.h"
#include "Color.h"
#include "Util.h"
#include "Version.h"
#include "MemoryDebug.h"
#include "Sphere.h"
#include "Scene.h"
#include "CGO.h"
#define XML_VERSION "1.0"
#define XML_ENCODING "UTF-8"
#define PRECISION 0.001f
#define TRANS_PRECISION 0.01f
/*
* Size of individual text nodes must be less than the maximum size for
* the LIBXML Parser node: 10MB. Text nodes longer than this will be split.
*/
#define XML_NODE_SIZE_LIMIT 1000000
/* For debugging output. */
#define COLLADA_DEBUG 0
/* Total number of `collada_geometry_mode` settings implemented. */
#define COLLADA_GEOM_MODE_COUNT 2
/* Returns a standard XML-formatted timestamp */
static char *GetTimestamp(){
time_t now;
struct tm *local;
char buffer[20];
time(&now);
local = localtime(&now);
strftime(buffer, 20, "%Y-%m-%dT%H:%M:%S", local);
return strdup(buffer);
}
/*
* Returns index of the first float in an array matching the given value to
* the given precision.
*/
static int GetFloatPositionInArray(float val, float *arr, int len,
float precision)
{
int i;
for (i = 0; i < len; i++) {
if (precision > fabsf(val - arr[i])){
return i;
}
}
return -1; // not found
}
/* Returns 1 if any of the 4 VLA strings has gotten too big, else 0. */
static int ReachedXmlNodeSizeLimit(int pos_str_cc, int norm_str_cc,
int col_str_cc, int p_str_cc)
{
int r = 0;
if(pos_str_cc >= XML_NODE_SIZE_LIMIT)
r = 1;
if(norm_str_cc >= XML_NODE_SIZE_LIMIT)
r = 1;
if(col_str_cc >= XML_NODE_SIZE_LIMIT)
r = 1;
if(p_str_cc >= XML_NODE_SIZE_LIMIT)
r = 1;
return r;
}
/* Writes global <asset> element with file meta information. */
static void ColladaWriteAssetElement(xmlTextWriterPtr w)
{
xmlTextWriterStartElement(w, BAD_CAST "asset");
xmlTextWriterStartElement(w, BAD_CAST "contributor");
xmlTextWriterWriteElement(w, BAD_CAST "author",
BAD_CAST "PyMOL User");
#ifdef _PyMOL_VERSION
char *vers = (char *)"PyMOL " _PyMOL_VERSION;
#else
char *vers = (char *)"PyMOL";
#endif
xmlTextWriterWriteElement(w, BAD_CAST "authoring_tool",
BAD_CAST vers);
xmlTextWriterEndElement(w); // contributor
char *ts = GetTimestamp();
xmlTextWriterWriteElement(w, BAD_CAST "created", BAD_CAST ts);
xmlTextWriterWriteElement(w, BAD_CAST "modified", BAD_CAST ts);
free(ts);
xmlTextWriterStartElement(w, BAD_CAST "unit");
xmlTextWriterWriteAttribute(w, BAD_CAST "meter", BAD_CAST "1e-10");
xmlTextWriterWriteAttribute(w, BAD_CAST "name", BAD_CAST "Angstrom");
xmlTextWriterEndElement(w); // unit
xmlTextWriterWriteElement(w, BAD_CAST "up_axis", BAD_CAST "Y_UP");
xmlTextWriterEndElement(w); // asset
}
/* Writes the <library_cameras> element, including PyMOL's viewport camera. */
static void ColladaWriteLibraryCameras(xmlTextWriterPtr w, PyMOLGlobals *G, int width,
int height, float fov, float front, float back)
{
SceneViewType view;
SceneGetView(G, view);
float aspect_ratio = (float)width / (float)height;
int ortho = SettingGetGlobal_i(G, cSetting_ortho);
int ray_ortho = SettingGetGlobal_i(G, cSetting_ray_orthoscopic);
if (ray_ortho == -1) {
ray_ortho = ortho;
}
xmlTextWriterStartElement(w, BAD_CAST "library_cameras");
xmlTextWriterStartElement(w, BAD_CAST "camera");
xmlTextWriterWriteAttribute(w, BAD_CAST "id", BAD_CAST "camera");
xmlTextWriterStartElement(w, BAD_CAST "optics");
xmlTextWriterStartElement(w, BAD_CAST "technique_common");
if (ray_ortho) {
float ymag = SettingGetGlobal_i(G, cSetting_field_of_view) / 2;
float ymag_scale = -view[18] / 50; // scale fov based on camera distance
float ortho_adjust = 0.88f; // prevent apparent size change
ymag = ymag * ymag_scale * ortho_adjust;
xmlTextWriterStartElement(w, BAD_CAST "orthographic");
xmlTextWriterWriteFormatElement(w, BAD_CAST "ymag", "%6.4f", ymag);
} else {
xmlTextWriterStartElement(w, BAD_CAST "perspective");
xmlTextWriterWriteFormatElement(w, BAD_CAST "yfov", "%6.4f", fov);
}
xmlTextWriterWriteFormatElement(w, BAD_CAST "aspect_ratio",
"%6.4f", aspect_ratio);
xmlTextWriterWriteFormatElement(w, BAD_CAST "znear", "%6.4f", front);
xmlTextWriterWriteFormatElement(w, BAD_CAST "zfar", "%6.4f", back);
xmlTextWriterEndElement(w); // perspective or orthographic
xmlTextWriterEndElement(w); // technique_common
xmlTextWriterEndElement(w); // optics
xmlTextWriterEndElement(w); // camera
xmlTextWriterEndElement(w); // library_cameras
}
/* Writes the <library_lights> element, including ambient and directional
* PyMOL lights. */
static void ColladaWriteLibraryLights(xmlTextWriterPtr w, CRay *I, PyMOLGlobals *G)
{
xmlTextWriterStartElement(w, BAD_CAST "library_lights");
/* Ambient light */
float ambient = SettingGetGlobal_f(I->G, cSetting_ambient);
if(ambient > 0.5){
ambient = 0.5;
}
/* RGB = "0.5 0.5 0.5" seems to work well; `ambient` setting is handled in
* default effect */
xmlTextWriterStartElement(w, BAD_CAST "light");
xmlTextWriterWriteAttribute(w, BAD_CAST "id", BAD_CAST "ambient-light");
xmlTextWriterStartElement(w, BAD_CAST "technique_common");
xmlTextWriterStartElement(w, BAD_CAST "ambient");
xmlTextWriterWriteElement(w, BAD_CAST "color", BAD_CAST "0.5 0.5 0.5");
xmlTextWriterEndElement(w); // directional
xmlTextWriterEndElement(w); // technique_common
xmlTextWriterEndElement(w); // light
/* All other lights are directional, and their intensities are equal. Use
* instances of this light in the <node> section below. */
xmlTextWriterStartElement(w, BAD_CAST "light");
xmlTextWriterWriteAttribute(w, BAD_CAST "id", BAD_CAST "pymol-light");
xmlTextWriterStartElement(w, BAD_CAST "technique_common");
xmlTextWriterStartElement(w, BAD_CAST "directional");
/* Intensity decreases with increasing number of lights. */
float intensity = SceneGetSpecularValue(G, 0.6, 10);
char *value = (char *) malloc(sizeof(char) * 30);
sprintf(value, "%5.3f %5.3f %5.3f", intensity, intensity, intensity);
xmlTextWriterWriteElement(w, BAD_CAST "color", BAD_CAST value);
free(value);
xmlTextWriterEndElement(w); // directional
xmlTextWriterEndElement(w); // technique_common
xmlTextWriterEndElement(w); // light
xmlTextWriterEndElement(w); // library_lights
}
/* Writes a <color>R G B</color> element. */
static void ColladaWriteCommonColorElement(xmlTextWriterPtr w, char *name,
char *sid, char *value)
{
xmlTextWriterStartElement(w, BAD_CAST name);
xmlTextWriterStartElement(w, BAD_CAST "color");
if(sid) {
xmlTextWriterWriteAttribute(w, BAD_CAST "sid", BAD_CAST sid);
}
else {
xmlTextWriterWriteAttribute(w, BAD_CAST "sid", BAD_CAST name);
}
xmlTextWriterWriteString(w, BAD_CAST value);
xmlTextWriterEndElement(w); // color
xmlTextWriterEndElement(w); // element
}
/* Writes a <float>value</float> element. */
static void ColladaWriteCommonFloatElement(xmlTextWriterPtr w, char *name,
char *sid, char *value)
{
xmlTextWriterStartElement(w, BAD_CAST name);
xmlTextWriterStartElement(w, BAD_CAST "float");
if(sid) {
xmlTextWriterWriteAttribute(w, BAD_CAST "sid", BAD_CAST sid);
}
else {
xmlTextWriterWriteAttribute(w, BAD_CAST "sid", BAD_CAST name);
}
xmlTextWriterWriteString(w, BAD_CAST value);
xmlTextWriterEndElement(w); // float
xmlTextWriterEndElement(w); // element
}
/* Writes a set of <phong> shader parameter elements. */
static void ColladaWritePhongEffect(xmlTextWriterPtr w, char *id,
float amb, float spec, float shin,
float trans, float iref)
{
char *value = (char *) malloc(100 * sizeof(char));
xmlTextWriterStartElement(w, BAD_CAST "effect");
xmlTextWriterWriteAttribute(w, BAD_CAST "id",
BAD_CAST id);
xmlTextWriterStartElement(w, BAD_CAST "profile_COMMON");
xmlTextWriterStartElement(w, BAD_CAST "technique");
xmlTextWriterWriteAttribute(w, BAD_CAST "sid",
BAD_CAST "common");
xmlTextWriterStartElement(w, BAD_CAST "phong");
if (amb > PRECISION) {
sprintf(value, "0.5 0.5 0.5 %5.3f", amb);
ColladaWriteCommonColorElement(w, (char *)"ambient", NULL, value);
}
if (spec > PRECISION) {
sprintf(value, "0.5 0.5 0.5 %5.3f", spec);
ColladaWriteCommonColorElement(w, (char *)"specular", NULL, value);
}
if(shin > PRECISION) {
sprintf(value, "%5.3f", shin);
ColladaWriteCommonFloatElement(w, (char *)"shininess", NULL, value);
}
if(trans > PRECISION) {
sprintf(value, "%5.3f", trans);
ColladaWriteCommonFloatElement(w, (char *)"transparency", NULL, value);
}
if(iref > PRECISION) {
sprintf(value, "%5.3f", iref);
ColladaWriteCommonFloatElement(w, (char *)"index_of_refraction", NULL, value);
}
xmlTextWriterEndElement(w); // phong
xmlTextWriterEndElement(w); // technique
xmlTextWriterEndElement(w); // profile_COMMON
xmlTextWriterEndElement(w); // effect
free(value);
}
/* Writes a <material> element as an instance of a specific <effect> by URL. */
static void ColladaWriteInstanceEffectMaterial(xmlTextWriterPtr w,
char *id, char *url)
{
xmlTextWriterStartElement(w, BAD_CAST "material");
xmlTextWriterWriteAttribute(w, BAD_CAST "id", BAD_CAST id);
xmlTextWriterStartElement(w, BAD_CAST "instance_effect");
xmlTextWriterWriteAttribute(w, BAD_CAST "url", BAD_CAST url);
xmlTextWriterEndElement(w); // instance_effect
xmlTextWriterEndElement(w); // material
}
/* Opens a <geometry id="..."><mesh> element. */
static void ColladaBeginGeometryMesh(xmlTextWriterPtr w, int geom){
xmlTextWriterStartElement(w, BAD_CAST "geometry");
xmlTextWriterWriteFormatAttribute(w, BAD_CAST "id", "geom%i", geom);
xmlTextWriterStartElement(w, BAD_CAST "mesh");
}
/* Closes a </mesh></geometry> element. */
static void ColladaEndGeometryMesh(xmlTextWriterPtr w){
xmlTextWriterEndElement(w); // mesh
xmlTextWriterEndElement(w); // geometry
}
/* Writes the <library_effects> element, including transparency values. */
static void ColladaWriteLibraryEffects(xmlTextWriterPtr w, PyMOLGlobals *G,
int trans_len, float *trans)
{
xmlTextWriterStartElement(w, BAD_CAST "library_effects");
float amb = SettingGetGlobal_f(G, cSetting_ambient);
if(amb > 0.5){
amb = 0.5;
}
float spec = SettingGetGlobal_f(G, cSetting_specular_intensity);
float shin_factor = 5.0f;
float shin = SettingGetGlobal_f(G, cSetting_shininess) / shin_factor;
/* Default effect */
ColladaWritePhongEffect(w, (char *)"default-effect", amb, spec, shin, 1, 1);
/* Background effect */
ColladaWritePhongEffect(w, (char *)"bg-effect", 0.5f, 0, 0, 0, 0);
/* Transparency effects */
int i;
char *name = (char *) malloc(100 * sizeof(char));
for (i = 0; i < trans_len; i++) {
sprintf(name, "transparency-%1.2f-effect", trans[i]);
ColladaWritePhongEffect(w, name, amb, spec, shin, 1 - trans[i], 1);
#if COLLADA_DEBUG
printf("Wrote Phong effect: %s\n", name);
#endif
}
xmlTextWriterEndElement(w); // library_effects
free(name);
}
/* Writes the <library_materials> element, including transparent materials. */
static void ColladaWriteLibraryMaterials(xmlTextWriterPtr w, int trans_len, float *trans)
{
xmlTextWriterStartElement(w, BAD_CAST "library_materials");
/* Default material */
ColladaWriteInstanceEffectMaterial(w, (char *)"default-material",
(char *)"#default-effect");
ColladaWriteInstanceEffectMaterial(w, (char *)"bg-material",
(char *)"#bg-effect");
/* Transparent materials */
int i;
char *name = (char *) malloc(100 * sizeof(char));
char *url = (char *) malloc(100 * sizeof(char));
for (i = 0; i < trans_len; i++) {
sprintf(name, "transparency-%1.2f-material", trans[i]);
sprintf(url, "#transparency-%1.2f-effect", trans[i]);
ColladaWriteInstanceEffectMaterial(w, name, url);
#if COLLADA_DEBUG
printf("Wrote material: %s for effect: %s\n", name, url);
#endif
}
xmlTextWriterEndElement(w); // library_materials
free(name);
free(url);
}
/*
* Writes a 3-dimensional <source> element, e.g. XYZ or RGB, to define
* position, normal, or color source data.
*
* Note: "count" should be the number of accessible elements (e.g. vertices,
* normals; NOT individual float values). The length of the float array will
* be 3x this value.
*/
static void ColladaWrite3DSource(xmlTextWriterPtr w, int geom, char *name, int count,
char *data_str, char *dim)
{
xmlTextWriterStartElement(w, BAD_CAST "source");
xmlTextWriterWriteFormatAttribute(w, BAD_CAST "id",
"geom%i-mesh-%s", geom, name);
xmlTextWriterStartElement(w, BAD_CAST "float_array");
xmlTextWriterWriteFormatAttribute(w, BAD_CAST "id",
"geom%i-mesh-%s-array", geom, name);
xmlTextWriterWriteFormatAttribute(w, BAD_CAST "count", "%i", count * 3);
xmlTextWriterWriteString(w, BAD_CAST data_str);
xmlTextWriterEndElement(w); // float_array
xmlTextWriterStartElement(w, BAD_CAST "technique_common");
xmlTextWriterStartElement(w, BAD_CAST "accessor");
xmlTextWriterWriteFormatAttribute(w, BAD_CAST "source",
"#geom%i-mesh-%s-array", geom, name);
xmlTextWriterWriteFormatAttribute(w, BAD_CAST "count", "%i", count);
xmlTextWriterWriteAttribute(w, BAD_CAST "stride", BAD_CAST "3");
char n[2] = "\0";
sprintf(n, "%c", dim[0]);
xmlTextWriterStartElement(w, BAD_CAST "param");
xmlTextWriterWriteAttribute(w, BAD_CAST "name", BAD_CAST n);
xmlTextWriterWriteAttribute(w, BAD_CAST "type", BAD_CAST "float");
xmlTextWriterEndElement(w);
sprintf(n, "%c", dim[1]);
xmlTextWriterStartElement(w, BAD_CAST "param");
xmlTextWriterWriteAttribute(w, BAD_CAST "name", BAD_CAST n);
xmlTextWriterWriteAttribute(w, BAD_CAST "type", BAD_CAST "float");
xmlTextWriterEndElement(w);
sprintf(n, "%c", dim[2]);
xmlTextWriterStartElement(w, BAD_CAST "param");
xmlTextWriterWriteAttribute(w, BAD_CAST "name", BAD_CAST n);
xmlTextWriterWriteAttribute(w, BAD_CAST "type", BAD_CAST "float");
xmlTextWriterEndElement(w);
xmlTextWriterEndElement(w); // accessor
xmlTextWriterEndElement(w); // technique_common
xmlTextWriterEndElement(w); // source
}
/* Write a <vertices> element using the current geometry's positions source. */
static void ColladaWriteVertices(xmlTextWriterPtr w, int geom)
{
xmlTextWriterStartElement(w, BAD_CAST "vertices");
xmlTextWriterWriteFormatAttribute(w, BAD_CAST "id",
"geom%i-mesh-vertices", geom);
xmlTextWriterStartElement(w, BAD_CAST "input");
xmlTextWriterWriteAttribute(w, BAD_CAST "semantic",
BAD_CAST "POSITION");
xmlTextWriterWriteFormatAttribute(w, BAD_CAST "source",
"#geom%i-mesh-positions", geom);
xmlTextWriterEndElement(w); // input
xmlTextWriterEndElement(w); // vertices
}
/* Writes vertex, normal, and color <input> elements for the given geometry. */
static void ColladaWriteVNCInputs(xmlTextWriterPtr w, int geom)
{
xmlTextWriterStartElement(w, BAD_CAST "input");
xmlTextWriterWriteAttribute(w, BAD_CAST "offset", BAD_CAST "0");
xmlTextWriterWriteAttribute(w, BAD_CAST "semantic",
BAD_CAST "VERTEX");
xmlTextWriterWriteFormatAttribute(w, BAD_CAST "source",
"#geom%i-mesh-vertices", geom);
xmlTextWriterEndElement(w); // input
xmlTextWriterStartElement(w, BAD_CAST "input");
xmlTextWriterWriteAttribute(w, BAD_CAST "offset", BAD_CAST "1");
xmlTextWriterWriteAttribute(w, BAD_CAST "semantic",
BAD_CAST "NORMAL");
xmlTextWriterWriteFormatAttribute(w, BAD_CAST "source",
"#geom%i-mesh-normals", geom);
xmlTextWriterEndElement(w); // input
xmlTextWriterStartElement(w, BAD_CAST "input");
xmlTextWriterWriteAttribute(w, BAD_CAST "offset", BAD_CAST "2");
xmlTextWriterWriteAttribute(w, BAD_CAST "semantic",
BAD_CAST "COLOR");
xmlTextWriterWriteFormatAttribute(w, BAD_CAST "source",
"#geom%i-mesh-colors", geom);
xmlTextWriterEndElement(w); // input
}
/* Writes a <vcount> element with the given string as its value. */
static void ColladaWriteVCountElement(xmlTextWriterPtr w, char *vcount_str)
{
xmlTextWriterStartElement(w, BAD_CAST "vcount");
xmlTextWriterWriteFormatString(w, "%s", vcount_str);
xmlTextWriterEndElement(w);
}
/* Writes a <vcount> element for the given number of triangles. */
static void ColladaWriteTrianglesVCountElement(xmlTextWriterPtr w, int tri)
{
int i;
char *vc_str = VLACalloc(char, 1000);
ov_size cc = 0;
char *next = (char *) malloc(10 * sizeof(char));
for(i = 0; i < tri; i++){
sprintf(next, "3 "); // all triangles
UtilConcatVLA(&vc_str, &cc, next);
}
ColladaWriteVCountElement(w, vc_str);
VLAFree(vc_str);
free(next);
}
/* Writes a <p> element with the given string as its value. */
static void ColladaWritePrimitiveElement(xmlTextWriterPtr w, char *p_str)
{
xmlTextWriterStartElement(w, BAD_CAST "p");
xmlTextWriterWriteFormatString(w, "%s", p_str);
xmlTextWriterEndElement(w); // p
}
/* Writes a <triangles> element with the current primitive (<p>) string. */
static void ColladaWriteTrianglesElement(xmlTextWriterPtr w, int geom, int tri,
char *p_str, int mode=0)
{
xmlTextWriterStartElement(w, BAD_CAST (mode == 1 ? "polylist" : "triangles"));
xmlTextWriterWriteFormatAttribute(w, BAD_CAST "count", "%i", tri);
xmlTextWriterWriteFormatAttribute(w, BAD_CAST "material",
"geom%i-material", geom);
ColladaWriteVNCInputs(w, geom);
if (mode == 1) {
ColladaWriteTrianglesVCountElement(w, tri);
}
ColladaWritePrimitiveElement(w, p_str);
xmlTextWriterEndElement(w); // triangles
}
/* Writes a <polylist> element consisting entirely of triangles. */
static void ColladaWriteTrianglesPolylistElement(xmlTextWriterPtr w, int geom, int tri,
char *p_str)
{
ColladaWriteTrianglesElement(w, geom, tri, p_str, 1);
}
/*
* Opens a <polylist> element, including <input>s. Must be followed by at
* least one <p> element and then closed.
*/
static void ColladaBeginPolylistElement(xmlTextWriterPtr w, int geom, int count)
{
xmlTextWriterStartElement(w, BAD_CAST "polylist");
xmlTextWriterWriteFormatAttribute(w, BAD_CAST "count", "%i", count);
xmlTextWriterWriteFormatAttribute(w, BAD_CAST "material",
"geom%i-material", geom);
ColladaWriteVNCInputs(w, geom);
}
/* Closes a <polylist> element. */
static void ColladaEndPolylistElement(xmlTextWriterPtr w)
{
xmlTextWriterEndElement(w);
}
/*
* Opens a <tristrips> element, including inputs. Must be followed by at
* least one <p> element. Multiple <p> elements can be used to have
* multiple triangle strips using the same vertices.
*/
static void ColladaBeginTristripsElement(xmlTextWriterPtr w, int geom, int num_strips)
{
xmlTextWriterStartElement(w, BAD_CAST "tristrips");
xmlTextWriterWriteFormatAttribute(w, BAD_CAST "count",
"%i", num_strips);
xmlTextWriterWriteFormatAttribute(w, BAD_CAST "material",
"geom%i-material", geom);
ColladaWriteVNCInputs(w, geom);
}
/* Closes a <tristrips> element. */
static void ColladaEndTristripsElement(xmlTextWriterPtr w)
{
xmlTextWriterEndElement(w);
}
#if 0
/*
* Opens a <trifans> element, including inputs. Must be followed by at
* least one <p> element.
*/
static void ColladaBeginTrifansElement(xmlTextWriterPtr w, int geom, int num_fans)
{
xmlTextWriterStartElement(w, BAD_CAST "trifans");
xmlTextWriterWriteFormatAttribute(w, BAD_CAST "count",
"%i", num_fans);
xmlTextWriterWriteFormatAttribute(w, BAD_CAST "material",
"geom%i-material", geom);
ColladaWriteVNCInputs(w, geom);
}
/* Closes a <trifans> element. */
static void ColladaEndTrifansElement(xmlTextWriterPtr w)
{
xmlTextWriterEndElement(w);
}
#endif
/* Writes a complete <geometry> element for a triangle mesh. */
static void ColladaWriteMeshGeometry(xmlTextWriterPtr w, int geom,
int pos, char* positions_str,
int norm, char* normals_str,
int col, char* colors_str,
int tri, char* p_str, int mode)
{
ColladaBeginGeometryMesh(w, geom);
ColladaWrite3DSource(w, geom, (char *)"positions", pos, positions_str,
(char *)"XYZ");
ColladaWrite3DSource(w, geom, (char *)"normals", norm, normals_str,
(char *)"XYZ");
ColladaWrite3DSource(w, geom, (char *)"colors", col, colors_str,
(char *)"RGB");
ColladaWriteVertices(w, geom);
ColladaWriteTrianglesElement(w, geom, tri, p_str, mode);
ColladaEndGeometryMesh(w);
}
#endif // _HAVE_LIBXML
/* Generates COLLADA output and appends it to `vla_ptr`. */
void RayRenderCOLLADA(CRay * I, int width, int height,
char **vla_ptr, float front, float back,
float fov)
{
#ifndef _HAVE_LIBXML
PRINTFB(I->G, FB_Ray, FB_Errors)
" ColladaRender-Error: No libxml2 support, can't output COLLADA file.\n"
ENDFB(I->G);
#elif !(defined(LIBXML_WRITER_ENABLED) && defined(LIBXML_OUTPUT_ENABLED))
PRINTFB(I->G, FB_Ray, FB_Errors)
" ColladaRender-Error: libxml2 library not properly configured " \
"with writer and output support, can't output COLLADA file.\n"
ENDFB(I->G);
#else
char *vla = *vla_ptr;
ov_size cc = 0; /* character count */
PyMOLGlobals *G = I->G;
/*
* Setting: geometry_export_mode
* -----------------------------
* 0 = Output everything. (default)
* 1 = Output geometry and materials only; exclude lighting and camera
* information from scene.
*/
int identity = (SettingGetGlobal_i(I->G, cSetting_geometry_export_mode) == 1);
/*
* Setting: collada_background_box
* -------------------------------
* 0 = Do not include a background box. (default)
* 1 = Include the background box for more accurate reproduction of the scene.
* NB: This setting is overridden by geometry_export_mode == 1, in which case
* no background box will be generated.
*/
int bgbox = SettingGetGlobal_i(I->G, cSetting_collada_background_box);
/*
* Setting: collada_geometry_mode
* ------------------------------
* 0 = Valid COLLADA 1.4.1. (default)
* 1 = Blender-compatible (only <polylist> geometries are used).
*/
int geom_mode = SettingGetGlobal_i(I->G, cSetting_collada_geometry_mode);
if (geom_mode >= COLLADA_GEOM_MODE_COUNT || geom_mode < 0) {
geom_mode = 0;
}
/*
* Setting: collada_export_lighting
* --------------------------------
* 0 = No lighting information included in output. Best for
* interactive scenes. (default)
* 1 = Lighting information (<library_lights> and light <node>s) included
* in output.
*/
int lighting = (SettingGetGlobal_i(I->G, cSetting_collada_export_lighting) == 1);
int lc = SettingGetGlobal_i(I->G, cSetting_light_count);
/* Ray trace */
RayExpandPrimitives(I);
RayTransformFirst(I, 0, identity);
RayComputeBox(I);
/* initialize for XML writing */
int rc; // return codes for error handling
xmlTextWriterPtr w;
xmlDocPtr doc;
xmlChar *tmp;
int buffersize;
/* Create a new XML DOM tree, to which the COLLADA document will be
* written */
doc = xmlNewDoc(BAD_CAST XML_VERSION);
if (doc == NULL) {
printf("ColladaRender: Error creating the xml document tree (xmlNewDoc).\n");
return;
}
/* Create a new XmlWriter */
w = xmlNewTextWriterTree(doc, NULL, 0);
if (w == NULL) {
printf("ColladaRender: Error creating the xml writer (xmlNewTextWriterTree).\n");
return;
}
/* Start the XML document */
rc = xmlTextWriterStartDocument(w, NULL, XML_ENCODING, NULL);
if (rc < 0) {
printf("ColladaRender: Error at xmlTextWriterStartDocument\n");
return;
}
/* Begin COLLADA */
xmlTextWriterStartElement(w, BAD_CAST "COLLADA");
xmlTextWriterWriteAttribute(w, BAD_CAST "xmlns",
BAD_CAST "http://www.collada.org/2005/11/COLLADASchema");
xmlTextWriterWriteAttribute(w, BAD_CAST "version", BAD_CAST "1.4.1");
/* Asset */
ColladaWriteAssetElement(w);
/* Cameras */
if (!identity) {
ColladaWriteLibraryCameras(w, G, width, height, fov, front, back);
}
/* Lights */
if (!identity && lighting) {
ColladaWriteLibraryLights(w, I, G);
}
/* Geometries */
int geom = 0;
/*
* Track transparency levels used on a per-geom basis to be added to
* additional library_effects and library_materials elements.
*/
float *trans = (float *) malloc(sizeof(float)); // store transparency values
int trans_len = 0;
/* Associate geometries with transparency values. */
int *geom_trans = (int *) malloc(sizeof(int));
int geom_trans_len = 1;
{
xmlTextWriterStartElement(w, BAD_CAST "library_geometries");
int a, tri, pos, norm, col;
CPrimitive *prim;
int mesh_obj = false;
int largest_dim = 10;
float cur_trans = 0.0f;
/* Initialize data string VLAs and character counts. */
char *positions_str = VLACalloc(char, 1000);
char *normals_str = VLACalloc(char, 1000);
char *colors_str = VLACalloc(char, 1000);
char *p_str = VLACalloc(char, 1000);
ov_size pos_str_cc = 0;
ov_size norm_str_cc = 0;
ov_size col_str_cc = 0;
ov_size p_str_cc = 0;
/*
* Loop through primitives, plus one extra time to finish writing a
* triangle mesh if necessary.
*/
for (a = 0; a <= I->NPrimitive; a++) {
prim = I->Primitive + a;
/* Handle transitions between/after triangle meshes. */
if (mesh_obj) {
if (a == I->NPrimitive ||
prim->type != cPrimTriangle ||
TRANS_PRECISION <= fabsf(prim->trans - cur_trans))
{
/*
* Not a triangle primitive, but the previous mesh is still
* active; OR, transparency level is different; OR the final
* primitive was part of a triangle mesh that needs to be
* closed.
*
* Write the previous triangle mesh from its data strings
* and counters, reset mesh tracking variables.
*/
ColladaWriteMeshGeometry(w, geom, pos, positions_str,
norm, normals_str, col, colors_str, tri, p_str, geom_mode);
geom += 1;
mesh_obj = false;
}
}
if(!mesh_obj) {
/* First triangle primitive or any other primititve. */
/* Allocate data strings */
pos_str_cc = 0;
norm_str_cc = 0;
col_str_cc = 0;
p_str_cc = 0;
if(a < I->NPrimitive){
/* Reset counters */
tri = 0;
pos = 0;
norm = 0;
col = 0;
cur_trans = prim->trans;
/* Increase geom_trans with geom, realloc exponentially. */
while (geom >= geom_trans_len) {
geom_trans_len *= 2;
geom_trans = (int *) realloc(geom_trans,
(geom_trans_len) * sizeof(int));
#if COLLADA_DEBUG
printf(" increased geom_trans_len to %i\n", geom_trans_len);
#endif
}
/* Record transparency for each geometry. */
int p = GetFloatPositionInArray(cur_trans, trans,
trans_len, TRANS_PRECISION);
if (0 > p) {
/* Not found, add new transparency level. */
trans = (float *) realloc(trans, (trans_len + 1) * sizeof(float));
trans[trans_len] = cur_trans;
#if COLLADA_DEBUG
printf(" Added new trans[%i] = %1.2f\n", trans_len,
trans[trans_len]);
#endif
geom_trans[geom] = trans_len;
trans_len++;
} else {
/* Reference existing transparency level. */
geom_trans[geom] = p;
}
#if COLLADA_DEBUG
printf("geom_trans[%i] = %i\n", geom, geom_trans[geom]);
#endif
if(prim->type == cPrimTriangle){
/* Track the open triangle mesh. */
mesh_obj = true;
}
}
else {
/*
* No active mesh object, and the last primitive has already been
* processed.
*/
/* Generate bounding box geometry for background. */
/* Only if collada_background_box == 1 and geometry_export_mode == 0 */
if (!identity && bgbox) {
/* Ensure camera is inside bounding box */
int i;
for (i = 0; i < 3; i++) {
if (fabsf(I->Pos[i]) > largest_dim) {
largest_dim = fabsf(I->Pos[i]);
}
}
/* Leave plenty of room */
largest_dim *= 100;
/* Allocate data strings */
pos_str_cc = 0;
norm_str_cc = 0;
col_str_cc = 0;
p_str_cc = 0;
int cube_coords[24] = {
1, 1, 1,
1, -1, 1,
-1, -1, 1,
-1, 1, 1,
-1, 1, -1,
-1, -1, -1,
1, -1, -1,
1, 1, -1 };
int cube_face_verts[24] = {
0, 1, 2, 3,
3, 2, 5, 4,
4, 5, 6, 7,
7, 6, 1, 0,
0, 3, 4, 7,
1, 6, 5, 2 };
char *next = (char *) malloc(200 * sizeof(int));
/* positions */
for(i = 0; i < 24; i++){
sprintf(next, "%i ", cube_coords[i] * largest_dim);
UtilConcatVLA(&positions_str, &pos_str_cc, next);
}
/* normals */
for(i = 0; i < 24; i++){
sprintf(next, "%i ", -cube_coords[i]);
UtilConcatVLA(&normals_str, &norm_str_cc, next);
}
/* color */
const float *bg_color;
bg_color = ColorGet(G, SettingGet_color(G, NULL, NULL, cSetting_bg_rgb));
sprintf(next, "%6.4f %6.4f %6.4f", bg_color[0], bg_color[1], bg_color[2]);
UtilConcatVLA(&colors_str, &col_str_cc, next);
/* p */
for(i = 0; i < 6; i++){
sprintf(next, "%i %i 0 %i %i 0 %i %i 0 %i %i 0 ",
cube_face_verts[4 * i], cube_face_verts[4 * i],
cube_face_verts[4 * i + 1], cube_face_verts[4 * i + 1],
cube_face_verts[4 * i + 2], cube_face_verts[4 * i + 2],
cube_face_verts[4 * i + 3], cube_face_verts[4 * i + 3]);
UtilConcatVLA(&p_str, &p_str_cc, next);
}
xmlTextWriterStartElement(w, BAD_CAST "geometry");
xmlTextWriterWriteAttribute(w, BAD_CAST "id", BAD_CAST "geom-bg");
xmlTextWriterStartElement(w, BAD_CAST "mesh");
ColladaWrite3DSource(w, geom, (char *)"positions", 8,
positions_str, (char *)"XYZ");
ColladaWrite3DSource(w, geom, (char *)"normals", 8,
normals_str, (char *)"XYZ");
ColladaWrite3DSource(w, geom, (char *)"colors", 1,
colors_str, (char *)"RGB");
ColladaWriteVertices(w, geom);
xmlTextWriterStartElement(w, BAD_CAST "polylist");
xmlTextWriterWriteAttribute(w, BAD_CAST "count", BAD_CAST "6");
xmlTextWriterWriteAttribute(w, BAD_CAST "material", BAD_CAST "geom-bg-material");
ColladaWriteVNCInputs(w, geom);
xmlTextWriterWriteElement(w, BAD_CAST "vcount", BAD_CAST "4 4 4 4 4 4");
ColladaWritePrimitiveElement(w, p_str);
xmlTextWriterEndElement(w); // polylist
xmlTextWriterEndElement(w); // mesh
xmlTextWriterEndElement(w); // geometry
free(next);
}
/* Finished with geometries. */
break;
}
}
/* Update largest dimension - assumes distance from origin for most
* objects will be much greater than e.g. sphere/cylinder/cone radius
* and camera won't be "too far" away. */
{
int i;
for(i = 0; i < 3; i++) {
switch (prim->type) {
/* 3 vertices defined */
case cPrimTriangle:
if (largest_dim < prim->v3[i]) {
largest_dim = prim->v3[i];
}
/* 2 vertices defined */
case cPrimCone:
case cPrimCylinder:
if (largest_dim < prim->v2[i]) {
largest_dim = prim->v2[i];
}
/* only 1 vertex defined */
case cPrimSphere:
default:
if (largest_dim < prim->v1[i]) {
largest_dim = prim->v1[i];
}
break;
}
}
}
/* Process the primitive according to its type. */
switch(prim->type){
case cPrimSphere: // 1
{
#if COLLADA_DEBUG > 1
printf("Primitive %i: cPrimSphere\n", a);
#endif
/* sphere_quality range: 0 to (NUMBER_OF_SPHERE_LEVELS - 1). */
int sq = (SettingGetGlobal_i(I->G, cSetting_sphere_quality));
SphereRec *sp;
int i, j;
int *q, *s;
char *next = (char *) malloc(200 * sizeof(char)); // enough for 9 %6.4 floats
sp = I->G->Sphere->Sphere[sq];
q = sp->Sequence;
s = sp->StripLen;
/* Generate <source> elements */
/* For each tristrip, add positions and normals to their respective
* data strings. */
for(i = 0; i < sp->NStrip; i++) {
#if COLLADA_DEBUG > 1
printf("\ni = %i, StripLen = %i\n", i, *s);
#endif
for(j = 0; j < (*s); j++) {
/* positions */
sprintf(next, "%6.4f %6.4f %6.4f ",
prim->v1[0] + (prim->r1 * sp->dot[*q][0]),
prim->v1[1] + (prim->r1 * sp->dot[*q][1]),
prim->v1[2] + (prim->r1 * sp->dot[*q][2]));
#if COLLADA_DEBUG > 1
printf("positions j = %02i: %s\n", j, next);
#endif
UtilConcatVLA(&positions_str, &pos_str_cc, next);
/* normals */
sprintf(next, "%6.4f %6.4f %6.4f ",
sp->dot[*q][0], sp->dot[*q][1], sp->dot[*q][2]);
UtilConcatVLA(&normals_str, &norm_str_cc, next);
q++; // next position in sequence
}
s++; // next strip
}
/* Colors: only one color per sphere. */
sprintf(next, "%6.4f %6.4f %6.4f",
prim->c1[0], prim->c1[1], prim->c1[2]);
UtilConcatVLA(&colors_str, &col_str_cc, next);
ColladaBeginGeometryMesh(w, geom);
ColladaWrite3DSource(w, geom, (char *)"positions", sp->NVertTot,
positions_str, (char *)"XYZ");
ColladaWrite3DSource(w, geom, (char *)"normals", sp->NVertTot,
normals_str, (char *)"XYZ");
ColladaWrite3DSource(w, geom, (char *)"colors", 1,
colors_str, (char *)"RGB");
ColladaWriteVertices(w, geom);
/* Reset to the beginning of the list of strips. */
q = sp->Sequence;
s = sp->StripLen;
int vert_ct;
vert_ct = 0;
if (geom_mode == 1) {
/* polylists */
int tri = 0;
for (i = 0; i < sp->NStrip; i++) {
/* Each triangle has the last 2 vertices of previous one as its
* first 2 vertices. Color is the same for all vertices. */
for(j = 2; j < (*s); j++) {
if (j % 2 == 0) {
/* even, normals correct as-is */
sprintf(next, "%i %i 0 %i %i 0 %i %i 0 ",
vert_ct + j - 2, vert_ct + j - 2,
vert_ct + j - 1, vert_ct + j - 1,
vert_ct + j, vert_ct + j);
} else {
/* odd, switch order to maintain correct normal */
sprintf(next, "%i %i 0 %i %i 0 %i %i 0 ",
vert_ct + j - 1, vert_ct + j - 1,
vert_ct + j - 2, vert_ct + j - 2,
vert_ct + j, vert_ct + j);
}
UtilConcatVLA(&p_str, &p_str_cc, next);
q++;
tri++;
}
ColladaWriteTrianglesPolylistElement(w, geom, tri, p_str);
vert_ct += (*s);
s++;
/* Get a fresh p_str for the next strip. */
VLAFree(p_str);
p_str = VLACalloc(char, 1000);
p_str_cc = 0;
tri = 0;
}
} else {
/* tristrips */
ColladaBeginTristripsElement(w, geom, sp->NStrip);
/* For each strip, write a <p> element. */
for(i = 0; i < sp->NStrip; i++) {
/* Write the 2 initial vertices. */
sprintf(next, "%i %i 0 %i %i 0 ",
vert_ct, vert_ct, vert_ct + 1, vert_ct + 1);
UtilConcatVLA(&p_str, &p_str_cc, next);
/* After the first 2, each new vertex adds a new triangle.
* Color is the same for all vertices. */
for(j = 2; j < (*s); j++) {
sprintf(next, "%i %i 0 ", vert_ct + j, vert_ct + j);
UtilConcatVLA(&p_str, &p_str_cc, next);
q++;
}
ColladaWritePrimitiveElement(w, p_str);
/* Start the next strip at the next vertex. */
vert_ct += *s;
s++;
/* Get a fresh p_str for the next strip. */
VLAFree(p_str);
p_str = VLACalloc(char, 1000);
p_str_cc = 0;
}
ColladaEndTristripsElement(w);
}
free(next);
ColladaEndGeometryMesh(w);
geom += 1;
break;
} // cPrimSphere
case cPrimCylinder: // 2
case cPrimSausage: // 4
case cPrimCone: // 7
{
#if COLLADA_DEBUG > 1
if(prim->type == cPrimCylinder) {
printf("Primitive %i: cPrimCylinder\n", a);
} else if(prim->type == cPrimSausage) {
printf("Primitive %i: cPrimSausage\n", a);
} else {
printf("Primitive %i: cPrimCone\n", a);
}
#endif
#define DAE_MAX_EDGE 50
/* Local vars */
float d[3], t[3], p0[3], p1[3], p2[3];
float v_buf[9], *v, vv1[3], vv2[3], vvv1[3], vvv2[3];
float x[DAE_MAX_EDGE + 1], y[DAE_MAX_EDGE + 1];
float overlap, overlap2, nub, nub2, r2 = 0.F;
int nEdge, c; //colorFlag;
int i;
char *next = (char *) malloc(200 * sizeof(char));
char *cap1_p_str = VLACalloc(char, 1000);
ov_size cap1_cc = 0;
bool stick_round_nub = SettingGetGlobal_i(I->G, cSetting_stick_round_nub);
const int j_arr[] = {2, 3, 2};
int nCapTri = 0;
int captype[2] = {prim->cap1, prim->cap2};
CGO *cgocap[2] = {NULL, NULL};
if(prim->type == cPrimSausage) {
captype[0] = captype[1] = cCylCapRound;
}
v = v_buf;
nEdge = SettingGetGlobal_i(I->G, cSetting_stick_quality);
overlap = prim->r1 * SettingGetGlobal_f(I->G, cSetting_stick_overlap);
nub = prim->r1 * SettingGetGlobal_f(I->G, cSetting_stick_nub);
if(prim->type == cPrimCone) {
r2 = prim->r2;
overlap2 = prim->r2 * SettingGetGlobal_f(I->G, cSetting_stick_overlap);
nub2 = prim->r2 * SettingGetGlobal_f(I->G, cSetting_stick_nub);
}
else {
r2 = prim->r1;
overlap2 = overlap;
nub2 = nub;
}
if(nEdge > DAE_MAX_EDGE)
nEdge = DAE_MAX_EDGE;
subdivide(nEdge, x, y);
//colorFlag = (prim->c1 != prim->c2) && prim->c2;
/* primary axis vector p0 */
p0[0] = (prim->v2[0] - prim->v1[0]);
p0[1] = (prim->v2[1] - prim->v1[1]);
p0[2] = (prim->v2[2] - prim->v1[2]);
normalize3f(p0);
/* cap1 */
copy3f(prim->v1, vv1);
copy3f(vv1, vvv1);
/* cap2 */
copy3f(prim->v2, vv2);
copy3f(vv2, vvv2);
d[0] = (vv2[0] - vv1[0]);
d[1] = (vv2[1] - vv1[1]);
d[2] = (vv2[2] - vv1[2]);
get_divergent3f(d, t);
/* orthogonal vectors p1, p2 */
cross_product3f(d, t, p1);
normalize3f(p1);
cross_product3f(d, p1, p2);
normalize3f(p2);
/* now we have a coordinate system */
/* cap1 */
if(captype[0] == cCylCapRound) {
if (stick_round_nub) {
cgocap[0] = CGONew(I->G);
CGORoundNub(cgocap[0], prim->v1, p0, p1, p2, -1, nEdge, prim->r1);
} else {
for(i = 0; i < 3; i++) {
vv1[i] -= p0[i] * overlap;
vvv1[i] = vv1[i] - p0[i] * nub;
}
}
}
/* cap2 */
if(captype[1] == cCylCapRound) {
if (stick_round_nub) {
cgocap[1] = CGONew(I->G);
CGORoundNub(cgocap[1], prim->v2, p0, p1, p2, 1, nEdge, prim->r1);
} else {
for(i = 0; i < 3; i++) {
vv2[i] += p0[i] * overlap2;
vvv2[i] = vv2[i] + p0[i] * nub2;
}
}
}
/* colors */
sprintf(next, "%6.4f %6.4f %6.4f %6.4f %6.4f %6.4f ",
prim->c1[0], prim->c1[1], prim->c1[2],
prim->c2[0], prim->c2[1], prim->c2[2]);
UtilConcatVLA(&colors_str, &col_str_cc, next);
/* Generate the data strings */
char *vcount_str = VLACalloc(char, 100);
ov_size vc_cc = 0;
{
/* Write values for each edge. The first edge is also the last
* edge. */
for(c = nEdge; c >= 0; c--) {
/* vector only, not positioned yet */
v[0] = p1[0] * x[c] + p2[0] * y[c];
v[1] = p1[1] * x[c] + p2[1] * y[c];
v[2] = p1[2] * x[c] + p2[2] * y[c];
/* vertices */
v[3] = vv1[0] + v[0] * prim->r1;
v[4] = vv1[1] + v[1] * prim->r1;
v[5] = vv1[2] + v[2] * prim->r1;
v[6] = vv2[0] + v[0] * r2;
v[7] = vv2[1] + v[1] * r2;
v[8] = vv2[2] + v[2] * r2;
/* positions */
sprintf(next, "%6.4f %6.4f %6.4f %6.4f %6.4f %6.4f ",
v[3], v[4], v[5], v[6], v[7], v[8]);
UtilConcatVLA(&positions_str, &pos_str_cc, next);
/* normals: Both vertices have the same normal. */
sprintf(next, "%6.4f %6.4f %6.4f ",
v[0], v[1], v[2]);
UtilConcatVLA(&normals_str, &norm_str_cc, next);
if (c > 0) {
/* vcount */
sprintf(next, "4 ");
UtilConcatVLA(&vcount_str, &vc_cc, next);
/* p for polylist */
sprintf(next, "%i %i %i %i %i %i %i %i %i %i %i %i ",
pos, norm, col,
pos + 1, norm, col + 1,
pos + 3, norm + 1, col,
pos + 2, norm + 1, col + 1);
UtilConcatVLA(&p_str, &p_str_cc, next);
}
pos += 2;
norm += 1;
}
/* Caps */
{
/* add another vertex-normal-color set for the center of each
* cap if r > 0 */
#define CONE_MIN_RADIUS 10e-6f
if(prim->r1 > CONE_MIN_RADIUS && !cgocap[0] && captype[0] != cCylCapNone){
/* positions */
sprintf(next, "%6.4f %6.4f %6.4f ",
vvv1[0], vvv1[1], vvv1[2]);
UtilConcatVLA(&positions_str, &pos_str_cc, next);
/* normals */
sprintf(next, "%6.4f %6.4f %6.4f ",
-p0[0], -p0[1], -p0[2]);
UtilConcatVLA(&normals_str, &norm_str_cc, next);
/* p */
for(i = 0; i < nEdge; i++) {
sprintf(next, "%i %i %i %i %i %i %i %i %i ",
pos, norm, col,
2*i, i, col,
2*i+2, i+1, col);
UtilConcatVLA(&cap1_p_str, &cap1_cc, next);
++nCapTri;
}
++pos;
++norm;
}
if(r2 > CONE_MIN_RADIUS && !cgocap[1] && captype[1] != cCylCapNone){
/* positions */
sprintf(next, "%6.4f %6.4f %6.4f ",
vvv2[0], vvv2[1], vvv2[2]);
UtilConcatVLA(&positions_str, &pos_str_cc, next);
/* normals */
sprintf(next, "%6.4f %6.4f %6.4f ",
p0[0], p0[1], p0[2]);
UtilConcatVLA(&normals_str, &norm_str_cc, next);
/* p */
for(i = 0; i < nEdge; i++) {
/* reverse order for other end */
sprintf(next, "%i %i %i %i %i %i %i %i %i ",
pos, norm, col + 1,
2*i+3, i+1, col + 1,
2*i+1, i, col + 1);
UtilConcatVLA(&cap1_p_str, &cap1_cc, next);
++nCapTri;
}
++pos;
++norm;
}
}
#if COLLADA_DEBUG > 1
printf("positions: %s\n", positions_str);
printf("normals: %s\n", normals_str);
printf("colors: %s\n", colors_str);
printf("p: %s\n", p_str);
#endif
for (i = 0; i < 2; ++i) {
if (!cgocap[i])
continue;
int pos0 = -1;
for (auto it = cgocap[i]->begin(); !it.is_stop(); ++it) {
auto pc = it.data();
int op = it.op_code();
switch (op){
case CGO_BEGIN:
pos0 = pos;
break;
case CGO_NORMAL:
/* normals */
sprintf(next, "%6.4f %6.4f %6.4f ", pc[0], pc[1], pc[2]);
UtilConcatVLA(&normals_str, &norm_str_cc, next);
++norm;
break;
case CGO_VERTEX:
/* positions */
sprintf(next, "%6.4f %6.4f %6.4f ", pc[0], pc[1], pc[2]);
UtilConcatVLA(&positions_str, &pos_str_cc, next);
++pos;
/* unroll the triangle strip */
if (pos > pos0 + 2) {
const int * j = j_arr + ((pos - pos0) % 2);
sprintf(next, "%i %i %i %i %i %i %i %i %i ",
pos - j[0], norm - j[0], col + i,
pos - j[1], norm - j[1], col + i,
pos - 1, norm - 1, col + i);
UtilConcatVLA(&cap1_p_str, &cap1_cc, next);
++nCapTri;
}
break;
}
}
CGOFree(cgocap[i]);
}
}
col += 2;
ColladaBeginGeometryMesh(w, geom);
ColladaWrite3DSource(w, geom, (char *)"positions", pos,
positions_str, (char *)"XYZ");
ColladaWrite3DSource(w, geom, (char *)"normals", norm,
normals_str, (char *)"XYZ");
ColladaWrite3DSource(w, geom, (char *)"colors", col,
colors_str, (char *)"RGB");
ColladaWriteVertices(w, geom);
/* polylist for cylinder shaft */
ColladaBeginPolylistElement(w, geom, nEdge);
ColladaWriteVCountElement(w, vcount_str);
ColladaWritePrimitiveElement(w, p_str);
ColladaEndPolylistElement(w);
if (nCapTri) {
ColladaWriteTrianglesElement(w, geom, nCapTri, cap1_p_str, geom_mode);
}
ColladaEndGeometryMesh(w);
geom += 1;
VLAFree(vcount_str);
VLAFree(cap1_p_str);
free(next);
break;
} // cPrimCylinder
case cPrimTriangle: // 3
{
#if COLLADA_DEBUG > 1
printf("Primitive %i: cPrimTriangle\n", a);
#endif
char *next = (char *) malloc(200 * sizeof(char)); // enough for 9 color floats
/*** Positions ***/
sprintf(next, "%6.4f %6.4f %6.4f %6.4f %6.4f %6.4f %6.4f %6.4f %6.4f ",
prim->v1[0], prim->v1[1], prim->v1[2],
prim->v2[0], prim->v2[1], prim->v2[2],
prim->v3[0], prim->v3[1], prim->v3[2]);
UtilConcatVLA(&positions_str, &pos_str_cc, (char *)next);
/*** Normals ***/
/* prim->n0 is a face normal; prim->n1/2/3 are vertex normals. */
sprintf(next, "%6.4f %6.4f %6.4f %6.4f %6.4f %6.4f %6.4f %6.4f %6.4f ",
prim->n1[0], prim->n1[1], prim->n1[2],
prim->n2[0], prim->n2[1], prim->n2[2],
prim->n3[0], prim->n3[1], prim->n3[2]);
UtilConcatVLA(&normals_str, &norm_str_cc, (char *)next);
/* Colors */
/* R, G, B per vertex */
sprintf(next, "%6.4f %6.4f %6.4f %6.4f %6.4f %6.4f %6.4f %6.4f %6.4f ",
prim->c1[0], prim->c1[1], prim->c1[2], // vertex 1
prim->c2[0], prim->c2[1], prim->c2[2], // vertex 2
prim->c3[0], prim->c3[1], prim->c3[2]); // vertex 3
UtilConcatVLA(&colors_str, &col_str_cc, next);
/* <p> indices */
if (TriangleReverse(prim)) {
sprintf(next, "%i %i %i %i %i %i %i %i %i ",
pos, norm, col,
pos + 2, norm + 2, col + 2,
pos + 1, norm + 1, col + 1);
} else {
sprintf(next, "%i %i %i %i %i %i %i %i %i ",
pos, norm, col,
pos + 1, norm + 1, col + 1,
pos + 2, norm + 2, col + 2);
}
UtilConcatVLA(&p_str, &p_str_cc, next);
pos += 3;
norm += 3;
col += 3;
tri += 1;
free(next);
if (ReachedXmlNodeSizeLimit(pos_str_cc, norm_str_cc,
col_str_cc, p_str_cc)) {
/* xmlTextWriter uses the LibXML parser module, which enforces a node size
* limit (10MB as of June 2014). Split oversize nodes into multiple nodes. */
#if COLLADA_DEBUG
printf("Reached XML_NODE_SIZE_LIMIT (%i) at a=%i, geom=%i\n",
XML_NODE_SIZE_LIMIT, a, geom);
#endif
/* Write the current mesh object and clean up */
ColladaWriteMeshGeometry(w, geom, pos, positions_str,
norm, normals_str, col, colors_str, tri, p_str, geom_mode);
geom += 1;
mesh_obj = false;
}
break;
} // cPrimTriangle
/* Character and Ellipsoid not implemented */
case cPrimCharacter: // 5
{
#if COLLADA_DEBUG > 1
printf("Primitive %i: cPrimCharacter\n", a);
#endif
break;
} // cPrimCharacter
case cPrimEllipsoid: // 6
{
#if COLLADA_DEBUG > 1
printf("Primitive %i: cPrimEllipsoid\n", a);
#endif
break;
} // cPrimEllipsoid
} // switch
} // for
xmlTextWriterEndElement(w); // library_geometries
VLAFree(positions_str);
VLAFree(normals_str);
VLAFree(colors_str);
VLAFree(p_str);
}
/* Effects */
ColladaWriteLibraryEffects(w, G, trans_len, trans);
/* Materials */
ColladaWriteLibraryMaterials(w, trans_len, trans);
/* Visual Scenes */
{
xmlTextWriterStartElement(w, BAD_CAST "library_visual_scenes");
// TODO: support for multiple scenes (e.g. stored scenes)
xmlTextWriterStartElement(w, BAD_CAST "visual_scene");
xmlTextWriterWriteAttribute(w, BAD_CAST "id", BAD_CAST "scene");
xmlTextWriterWriteAttribute(w, BAD_CAST "name", BAD_CAST "scene");
/* One node per geometry element. */
int i, j;
char *mat = (char *) malloc(100 * sizeof(char));
for(i = 0; i < geom; i++){
xmlTextWriterStartElement(w, BAD_CAST "node");
xmlTextWriterWriteFormatAttribute(w, BAD_CAST "id", "node-geom%i", i);
/* set center screen pixel as origin */
float offset[3];
SceneOriginGet(G, offset);
invert3f(offset);
for (j = 0; j < 2; j++) {
/* camera XY offset (Z handled in camera node) */
offset[j] += I->Pos[j];
}
char *tmp = (char *) malloc(50 * sizeof(char));
sprintf(tmp, "%5.3f %5.3f %5.3f", offset[0], offset[1], offset[2]);
xmlTextWriterStartElement(w, BAD_CAST "translate");
xmlTextWriterWriteAttribute(w, BAD_CAST "sid", BAD_CAST "location");
xmlTextWriterWriteString(w, BAD_CAST tmp);
xmlTextWriterEndElement(w); // translate
free(tmp);
xmlTextWriterStartElement(w, BAD_CAST "rotate");
xmlTextWriterWriteAttribute(w, BAD_CAST "sid", BAD_CAST "rotationZ");
xmlTextWriterWriteString(w, BAD_CAST "0 0 1 0");
xmlTextWriterEndElement(w); // rotate
xmlTextWriterStartElement(w, BAD_CAST "rotate");
xmlTextWriterWriteAttribute(w, BAD_CAST "sid", BAD_CAST "rotationY");
xmlTextWriterWriteString(w, BAD_CAST "0 1 0 0");
xmlTextWriterEndElement(w); // rotate
xmlTextWriterStartElement(w, BAD_CAST "rotate");
xmlTextWriterWriteAttribute(w, BAD_CAST "sid", BAD_CAST "rotationX");
xmlTextWriterWriteString(w, BAD_CAST "1 0 0 0");
xmlTextWriterEndElement(w); // rotate
xmlTextWriterStartElement(w, BAD_CAST "scale");
xmlTextWriterWriteAttribute(w, BAD_CAST "sid", BAD_CAST "scale");
xmlTextWriterWriteString(w, BAD_CAST "1 1 1");
xmlTextWriterEndElement(w); // scale
xmlTextWriterStartElement(w, BAD_CAST "instance_geometry");
xmlTextWriterWriteFormatAttribute(w, BAD_CAST "url", "#geom%i", i);
xmlTextWriterStartElement(w, BAD_CAST "bind_material");
xmlTextWriterStartElement(w, BAD_CAST "technique_common");
xmlTextWriterStartElement(w, BAD_CAST "instance_material");
xmlTextWriterWriteFormatAttribute(w, BAD_CAST "symbol",
"geom%i-material", i);
if (TRANS_PRECISION > geom_trans[i]) {
sprintf(mat, "#default-material");
}
else {
sprintf(mat, "#transparency-%1.2f-material", trans[geom_trans[i]]);
}
xmlTextWriterWriteAttribute(w, BAD_CAST "target", BAD_CAST mat);
xmlTextWriterEndElement(w); // instance_material
xmlTextWriterEndElement(w); // technique_common
xmlTextWriterEndElement(w); // bind_material
xmlTextWriterEndElement(w); // instance_geometry
xmlTextWriterEndElement(w); // node
}
free(mat);
/* background geometry */
if (!identity && bgbox) {
xmlTextWriterStartElement(w, BAD_CAST "node");
xmlTextWriterWriteAttribute(w, BAD_CAST "id", BAD_CAST "node-geom-bg");
xmlTextWriterStartElement(w, BAD_CAST "translate");
xmlTextWriterWriteAttribute(w, BAD_CAST "sid", BAD_CAST "location");
xmlTextWriterWriteString(w, BAD_CAST "0 0 0");
xmlTextWriterEndElement(w); // translate
xmlTextWriterStartElement(w, BAD_CAST "rotate");
xmlTextWriterWriteAttribute(w, BAD_CAST "sid", BAD_CAST "rotationZ");
xmlTextWriterWriteString(w, BAD_CAST "0 0 1 0");
xmlTextWriterEndElement(w); // rotate
xmlTextWriterStartElement(w, BAD_CAST "rotate");
xmlTextWriterWriteAttribute(w, BAD_CAST "sid", BAD_CAST "rotationY");
xmlTextWriterWriteString(w, BAD_CAST "0 1 0 0");
xmlTextWriterEndElement(w); // rotate
xmlTextWriterStartElement(w, BAD_CAST "rotate");
xmlTextWriterWriteAttribute(w, BAD_CAST "sid", BAD_CAST "rotationX");
xmlTextWriterWriteString(w, BAD_CAST "1 0 0 0");
xmlTextWriterEndElement(w); // rotate
xmlTextWriterStartElement(w, BAD_CAST "scale");
xmlTextWriterWriteAttribute(w, BAD_CAST "sid", BAD_CAST "scale");
xmlTextWriterWriteString(w, BAD_CAST "1 1 1");
xmlTextWriterEndElement(w); // scale
xmlTextWriterStartElement(w, BAD_CAST "instance_geometry");
xmlTextWriterWriteAttribute(w, BAD_CAST "url", BAD_CAST "#geom-bg");
xmlTextWriterStartElement(w, BAD_CAST "bind_material");
xmlTextWriterStartElement(w, BAD_CAST "technique_common");
xmlTextWriterStartElement(w, BAD_CAST "instance_material");
xmlTextWriterWriteAttribute(w, BAD_CAST "symbol",
BAD_CAST "geom-bg-material");
xmlTextWriterWriteAttribute(w, BAD_CAST "target",
BAD_CAST "#bg-material"); // TODO: custom materials
xmlTextWriterEndElement(w); // instance_material
xmlTextWriterEndElement(w); // technique_common
xmlTextWriterEndElement(w); // bind_material
xmlTextWriterEndElement(w); // instance_geometry
xmlTextWriterEndElement(w); // node
}
/* Camera node */
char *matrix = (char *) malloc(400 * sizeof(char)); // enough for 16 floats
if (!identity) {
float m1[16];
// m1 = RotMatrix * T(0, 0, -Pos[z])
identity44f(m1);
// camera z
m1[11] = -I->Pos[2];
// RotMatrix
left_multiply44f44f(SceneGetMatrix(G), m1);
sprintf(matrix,
"\n%5.3f %5.3f %5.3f %5.3f "
"\n%5.3f %5.3f %5.3f %5.3f "
"\n%5.3f %5.3f %5.3f %5.3f "
"\n%5.3f %5.3f %5.3f %5.3f ",
m1[0], m1[1], m1[2], m1[3],
m1[4], m1[5], m1[6], m1[7],
m1[8], m1[9], m1[10], m1[11],
m1[12], m1[13], m1[14], m1[15]);
xmlTextWriterStartElement(w, BAD_CAST "node");
xmlTextWriterWriteAttribute(w, BAD_CAST "id", BAD_CAST "node-camera");
xmlTextWriterWriteElement(w, BAD_CAST "matrix", BAD_CAST matrix);
xmlTextWriterStartElement(w, BAD_CAST "scale");
xmlTextWriterWriteAttribute(w, BAD_CAST "sid", BAD_CAST "scale");
xmlTextWriterWriteString(w, BAD_CAST "1 1 1");
xmlTextWriterEndElement(w); // scale
xmlTextWriterStartElement(w, BAD_CAST "instance_camera");
xmlTextWriterWriteAttribute(w, BAD_CAST "url", BAD_CAST "#camera");
xmlTextWriterEndElement(w); // instance_camera
xmlTextWriterEndElement(w); // node
}
/* Light nodes */
if (!identity && lighting) {
if(lc > 0){
/* First light is the ambient light. For light_count of 2-10,
* we add a directional light, e.g. light, light2, etc. */
int i;
xmlTextWriterStartElement(w, BAD_CAST "node");
xmlTextWriterWriteFormatAttribute(w, BAD_CAST "id", "node-ambient-light");
xmlTextWriterWriteAttribute(w, BAD_CAST "type", BAD_CAST "NODE");
xmlTextWriterStartElement(w, BAD_CAST "instance_light");
xmlTextWriterWriteAttribute(w, BAD_CAST "url", BAD_CAST "#ambient-light");
xmlTextWriterEndElement(w); // instance_light
xmlTextWriterEndElement(w); // node
const float *light_pos_ptr;
float light_pos[3];
for(i = 1; i < lc; i++){
switch(i){
case 1:
light_pos_ptr = SettingGetGlobal_3fv(G, cSetting_light);
break;
case 2:
light_pos_ptr = SettingGetGlobal_3fv(G, cSetting_light2);
break;
case 3:
light_pos_ptr = SettingGetGlobal_3fv(G, cSetting_light3);
break;
case 4:
light_pos_ptr = SettingGetGlobal_3fv(G, cSetting_light4);
break;
case 5:
light_pos_ptr = SettingGetGlobal_3fv(G, cSetting_light5);
break;
case 6:
light_pos_ptr = SettingGetGlobal_3fv(G, cSetting_light6);
break;
case 7:
light_pos_ptr = SettingGetGlobal_3fv(G, cSetting_light7);
break;
case 8:
light_pos_ptr = SettingGetGlobal_3fv(G, cSetting_light8);
break;
default:
light_pos_ptr = SettingGetGlobal_3fv(G, cSetting_light9);
break;
}
copy3f(light_pos_ptr, light_pos);
normalize3f(light_pos);
xmlTextWriterStartElement(w, BAD_CAST "node");
xmlTextWriterWriteFormatAttribute(w, BAD_CAST "id", "node-pymol-light%i", i);
xmlTextWriterWriteAttribute(w, BAD_CAST "type", BAD_CAST "NODE");
char *lookat = (char *) malloc(sizeof(char) * 50);
sprintf(lookat,
"%5.3f %5.3f %5.3f " // position of light on unit sphere
"0 0 0 " // pointed toward origin
"0 1 0", // positive y axis up (arbitrary)
/* negative because PyMOL gives direction of light, not position */
-light_pos[0], -light_pos[1], -light_pos[2]);
xmlTextWriterWriteElement(w, BAD_CAST "lookat", BAD_CAST lookat);
xmlTextWriterWriteElement(w, BAD_CAST "matrix", BAD_CAST matrix);
xmlTextWriterStartElement(w, BAD_CAST "instance_light");
xmlTextWriterWriteAttribute(w, BAD_CAST "url", BAD_CAST "#pymol-light");
xmlTextWriterEndElement(w); // instance_light
xmlTextWriterEndElement(w); // node
}
}
}
free(matrix);
xmlTextWriterEndElement(w); // visual_scene
xmlTextWriterEndElement(w); // library_visual_scenes
}
/* Scene */
{
xmlTextWriterStartElement(w, BAD_CAST "scene");
xmlTextWriterStartElement(w, BAD_CAST "instance_visual_scene");
xmlTextWriterWriteAttribute(w, BAD_CAST "url", BAD_CAST "#scene");
xmlTextWriterEndElement(w); // instance_visual_scene
xmlTextWriterEndElement(w); // scene
}
xmlTextWriterEndElement(w); // COLLADA
/* Close the document */
rc = xmlTextWriterEndDocument(w);
if (rc < 0) {
printf("ColladaRender: Error at xmlTextWriterEndDocument.\n");
// return;
}
/*
* Dump the document to buffer
*/
xmlDocDumpFormatMemory(doc, &tmp, &buffersize, 1);
/* Concat buffer to VLA output */
UtilConcatVLA(&vla, &cc, (char *) tmp);
/*
* Free associated memory.
*/
free(trans);
free(geom_trans);
xmlFree(tmp);
xmlFreeTextWriter(w);
xmlFreeDoc(doc);
*vla_ptr = vla;
#endif // _HAVE_LIBXML
}