# generat3D - evaluate Daz DSON scenes and write .dbz files # Copyright (C) 2026 Danilo M. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import gzip import json import math import os import tempfile from pathlib import Path from . import __version__ from .evaluate import evaluate def orientation_quat(deg): """DSON orientation (Euler degrees, XYZ) to a quaternion [x, y, z, w].""" hx, hy, hz = (math.radians(d) / 2 for d in deg) cx, sx = math.cos(hx), math.sin(hx) cy, sy = math.cos(hy), math.sin(hy) cz, sz = math.cos(hz), math.sin(hz) # q = qz * qy * qx (X applied first) return [sx * cy * cz - cx * sy * sz, cx * sy * cz + sx * cy * sz, cx * cy * sz - sx * sy * cz, cx * cy * cz + sx * sy * sz] def _node(n, r): return {"name": n.name, "id": n.id, "label": n.label, "center_point": r["center_point"], "end_point": r["end_point"], "orientation": orientation_quat(r["orientation"]), "rotation_order": n.rotation_order} def build(scene, roots): """The .dbz document for a loaded scene.""" verts, rest = evaluate(scene) fig = scene.figure root, bones = fig.nodes[0], fig.nodes[1:] entry = _node(root, rest[root.id]) entry.update({ "class": "DzFigure", "material groups": fig.material_groups, "num verts": len(verts), "vertices": verts.tolist(), "bones": [_node(b, rest[b.id]) | {"origin": rest[b.id]["center_point"]} for b in bones], }) return {"application": "export_to_blender", "version": f"generat3d {__version__}", "filepath": str(scene.path), "rootpaths": {"content": [str(r) for r in roots]}, "figures": [entry, {"name": "dummy", "num verts": 0}]} def write(doc, out): """Write doc as gzipped JSON. A temp file plus rename: no partial file on failure.""" out = Path(out) fd, tmp = tempfile.mkstemp(dir=out.parent, prefix=out.name, suffix=".tmp") try: with os.fdopen(fd, "wb") as f: f.write(gzip.compress(json.dumps(doc).encode())) os.replace(tmp, out) except BaseException: os.unlink(tmp) raise