Task 124: .DAE File Format

Task 124: .DAE File Format

File Format Specifications for .DAE

The .DAE (Digital Asset Exchange) file format is based on the COLLADA specification, which is an XML-based schema for exchanging 3D assets between applications. It is managed by the Khronos Group and supports elements for geometry, animation, shaders, physics, and metadata. The format is text-based (XML), with a root element <COLLADA> and a version attribute (e.g., 1.4.1 or 1.5.0). Key versions include COLLADA 1.4 (widely adopted) and 1.5 (with enhancements like geolocation and kinematics). The official specifications are available as PDFs:

List of Properties Intrinsic to the .DAE File Format

These properties are derived from the <asset> element in the COLLADA schema, which provides metadata intrinsic to the file. The <asset> element is optional but commonly used for asset management, including authorship, timestamps, and coordinate system details. Below is a comprehensive list of properties (child elements and their sub-properties) from the specification:

  • Contributor (0 or more instances): Represents authorship and tool information.
  • Author: The name of the author.
  • Author Email: The author's email address.
  • Author Website: The author's website URL.
  • Authoring Tool: The name of the tool used to create the asset.
  • Comments: Additional comments from the contributor.
  • Copyright: Copyright information.
  • Source Data: URI to source data.
  • Coverage (0 or 1): Defines spatial coverage.
  • Geographic Location:
  • Longitude: Float value (-180.0 to 180.0).
  • Latitude: Float value (-90.0 to 90.0).
  • Altitude: Float value, with mode (absolute or relativeToGround).
  • Created: Date and time the asset was created (ISO 8601 xs:dateTime format).
  • Keywords: Space-separated list of search keywords.
  • Modified: Date and time the asset was last modified (ISO 8601 xs:dateTime format).
  • Revision: Revision number or identifier.
  • Subject: Topical description of the asset.
  • Title: Title of the asset.
  • Unit: Defines the unit of distance.
  • Name: String (e.g., "meter").
  • Meter: Float value representing meters per unit (default 1.0).
  • Up Axis: Defines the upward axis (e.g., "Y_UP", "Z_UP").

These properties are not file system metadata (like size or permissions) but are embedded in the XML structure, making them intrinsic to the .DAE format itself.

Two Direct Download Links for .DAE Files

HTML/JavaScript for Drag-and-Drop .DAE Property Dump

Below is a self-contained HTML page with embedded JavaScript. It allows users to drag and drop a .DAE file, parses the XML, extracts the properties from the <asset> element, and dumps them to the screen in a readable format. (Note: This assumes modern browsers with FileReader and DOMParser support.)

DAE Property Dumper
Drag and drop a .DAE file here

Python Class for .DAE Properties

Below is a Python class that opens a .DAE file, decodes/parses the XML, reads and prints the properties to console, and includes a method to write a modified file with updated properties.

import xml.etree.ElementTree as ET
from datetime import datetime

class DAEProcessor:
    NS = '{http://www.collada.org/2005/11/COLLADASchema}'  # For COLLADA 1.4
    NS15 = '{http://www.collada.org/2008/03/COLLADASchema}'  # For COLLADA 1.5

    def __init__(self, filepath):
        self.filepath = filepath
        self.tree = None
        self.root = None
        self.asset = None
        self._load()

    def _load(self):
        self.tree = ET.parse(self.filepath)
        self.root = self.tree.getroot()
        # Try both namespaces
        self.asset = self.root.find('asset') or self.root.find(self.NS + 'asset') or self.root.find(self.NS15 + 'asset')

    def print_properties(self):
        if not self.asset:
            print("No <asset> element found.")
            return

        print("DAE Properties:")

        # Contributors
        contributors = self.asset.findall('contributor') or self.asset.findall(self.NS + 'contributor') or self.asset.findall(self.NS15 + 'contributor')
        for i, contrib in enumerate(contributors, 1):
            print(f"\nContributor {i}:")
            print(f"  Author: {contrib.findtext('author') or contrib.findtext(self.NS + 'author') or contrib.findtext(self.NS15 + 'author') or 'N/A'}")
            print(f"  Author Email: {contrib.findtext('author_email') or contrib.findtext(self.NS + 'author_email') or contrib.findtext(self.NS15 + 'author_email') or 'N/A'}")
            print(f"  Author Website: {contrib.findtext('author_website') or contrib.findtext(self.NS + 'author_website') or contrib.findtext(self.NS15 + 'author_website') or 'N/A'}")
            print(f"  Authoring Tool: {contrib.findtext('authoring_tool') or contrib.findtext(self.NS + 'authoring_tool') or contrib.findtext(self.NS15 + 'authoring_tool') or 'N/A'}")
            print(f"  Comments: {contrib.findtext('comments') or contrib.findtext(self.NS + 'comments') or contrib.findtext(self.NS15 + 'comments') or 'N/A'}")
            print(f"  Copyright: {contrib.findtext('copyright') or contrib.findtext(self.NS + 'copyright') or contrib.findtext(self.NS15 + 'copyright') or 'N/A'}")
            print(f"  Source Data: {contrib.findtext('source_data') or contrib.findtext(self.NS + 'source_data') or contrib.findtext(self.NS15 + 'source_data') or 'N/A'}")

        # Geographic Location
        geo = self.asset.find('.//geographic_location') or self.asset.find(self.NS + './/geographic_location') or self.asset.find(self.NS15 + './/geographic_location')
        if geo:
            print("\nGeographic Location:")
            print(f"  Longitude: {geo.findtext('longitude') or geo.findtext(self.NS + 'longitude') or geo.findtext(self.NS15 + 'longitude') or 'N/A'}")
            print(f"  Latitude: {geo.findtext('latitude') or geo.findtext(self.NS + 'latitude') or geo.findtext(self.NS15 + 'latitude') or 'N/A'}")
            alt = geo.find('altitude') or geo.find(self.NS + 'altitude') or geo.find(self.NS15 + 'altitude')
            print(f"  Altitude: {alt.text if alt else 'N/A'} (Mode: {alt.get('mode') if alt else 'N/A'})")

        # Other properties
        print(f"\nCreated: {self.asset.findtext('created') or self.asset.findtext(self.NS + 'created') or self.asset.findtext(self.NS15 + 'created') or 'N/A'}")
        print(f"Keywords: {self.asset.findtext('keywords') or self.asset.findtext(self.NS + 'keywords') or self.asset.findtext(self.NS15 + 'keywords') or 'N/A'}")
        print(f"Modified: {self.asset.findtext('modified') or self.asset.findtext(self.NS + 'modified') or self.asset.findtext(self.NS15 + 'modified') or 'N/A'}")
        print(f"Revision: {self.asset.findtext('revision') or self.asset.findtext(self.NS + 'revision') or self.asset.findtext(self.NS15 + 'revision') or 'N/A'}")
        print(f"Subject: {self.asset.findtext('subject') or self.asset.findtext(self.NS + 'subject') or self.asset.findtext(self.NS15 + 'subject') or 'N/A'}")
        print(f"Title: {self.asset.findtext('title') or self.asset.findtext(self.NS + 'title') or self.asset.findtext(self.NS15 + 'title') or 'N/A'}")

        # Unit
        unit = self.asset.find('unit') or self.asset.find(self.NS + 'unit') or self.asset.find(self.NS15 + 'unit')
        print(f"Unit: Name={unit.get('name') if unit else 'N/A'}, Meter={unit.get('meter') if unit else 'N/A'}")

        # Up Axis
        print(f"Up Axis: {self.asset.findtext('up_axis') or self.asset.findtext(self.NS + 'up_axis') or self.asset.findtext(self.NS15 + 'up_axis') or 'N/A'}")

    def update_and_write(self, new_filepath, updates={}):
        if not self.asset:
            print("No <asset> to update.")
            return
        # Example updates: {'created': new_value, 'modified': datetime.now().isoformat(), etc.}
        for key, value in updates.items():
            elem = self.asset.find(key) or self.asset.find(self.NS + key) or self.asset.find(self.NS15 + key)
            if elem is not None:
                elem.text = value
        self.tree.write(new_filepath, encoding='utf-8', xml_declaration=True)

# Usage example:
# processor = DAEProcessor('example.dae')
# processor.print_properties()
# processor.update_and_write('modified.dae', {'modified': datetime.now().isoformat()})

Java Class for .DAE Properties

Below is a Java class that opens a .DAE file, decodes/parses the XML, reads and prints the properties to console, and includes a method to write a modified file.

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import java.io.File;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class DAEProcessor {
    private Document doc;
    private Element asset;

    public DAEProcessor(String filepath) throws Exception {
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        DocumentBuilder db = dbf.newDocumentBuilder();
        doc = db.parse(new File(filepath));
        asset = (Element) doc.getElementsByTagName("asset").item(0);
    }

    public void printProperties() {
        if (asset == null) {
            System.out.println("No <asset> element found.");
            return;
        }

        System.out.println("DAE Properties:");

        // Contributors
        NodeList contributors = asset.getElementsByTagName("contributor");
        for (int i = 0; i < contributors.getLength(); i++) {
            Element contrib = (Element) contributors.item(i);
            System.out.println("\nContributor " + (i + 1) + ":");
            System.out.println("  Author: " + getText(contrib, "author"));
            System.out.println("  Author Email: " + getText(contrib, "author_email"));
            System.out.println("  Author Website: " + getText(contrib, "author_website"));
            System.out.println("  Authoring Tool: " + getText(contrib, "authoring_tool"));
            System.out.println("  Comments: " + getText(contrib, "comments"));
            System.out.println("  Copyright: " + getText(contrib, "copyright"));
            System.out.println("  Source Data: " + getText(contrib, "source_data"));
        }

        // Geographic Location
        Element geo = (Element) asset.getElementsByTagName("geographic_location").item(0);
        if (geo != null) {
            System.out.println("\nGeographic Location:");
            System.out.println("  Longitude: " + getText(geo, "longitude"));
            System.out.println("  Latitude: " + getText(geo, "latitude"));
            Element alt = (Element) geo.getElementsByTagName("altitude").item(0);
            System.out.println("  Altitude: " + (alt != null ? alt.getTextContent() : "N/A") + " (Mode: " + (alt != null ? alt.getAttribute("mode") : "N/A") + ")");
        }

        // Other properties
        System.out.println("\nCreated: " + getText(asset, "created"));
        System.out.println("Keywords: " + getText(asset, "keywords"));
        System.out.println("Modified: " + getText(asset, "modified"));
        System.out.println("Revision: " + getText(asset, "revision"));
        System.out.println("Subject: " + getText(asset, "subject"));
        System.out.println("Title: " + getText(asset, "title"));

        // Unit
        Element unit = (Element) asset.getElementsByTagName("unit").item(0);
        System.out.println("Unit: Name=" + (unit != null ? unit.getAttribute("name") : "N/A") + ", Meter=" + (unit != null ? unit.getAttribute("meter") : "N/A"));

        // Up Axis
        System.out.println("Up Axis: " + getText(asset, "up_axis"));
    }

    private String getText(Element parent, String tag) {
        NodeList nodes = parent.getElementsByTagName(tag);
        return nodes.getLength() > 0 ? nodes.item(0).getTextContent() : "N/A";
    }

    public void updateAndWrite(String newFilepath, String key, String value) throws Exception {
        if (asset == null) {
            System.out.println("No <asset> to update.");
            return;
        }
        NodeList nodes = asset.getElementsByTagName(key);
        if (nodes.getLength() > 0) {
            nodes.item(0).setTextContent(value);
        }
        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer transformer = tf.newTransformer();
        DOMSource source = new DOMSource(doc);
        StreamResult result = new StreamResult(new File(newFilepath));
        transformer.transform(source, result);
    }

    // Usage example:
    // public static void main(String[] args) throws Exception {
    //     DAEProcessor processor = new DAEProcessor("example.dae");
    //     processor.printProperties();
    //     processor.updateAndWrite("modified.dae", "modified", LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME));
    // }
}

JavaScript Class for .DAE Properties

Below is a JavaScript class (for Node.js or browser with fs polyfill) that opens a .DAE file, decodes/parses the XML, reads and prints the properties to console, and includes a method to write a modified file. (For browser, adjust fs to local storage or blob.)

const fs = require('fs'); // For Node.js
const DOMParser = require('xmldom').DOMParser; // For Node.js; in browser, use window.DOMParser
const XMLSerializer = require('xmldom').XMLSerializer;

class DAEProcessor {
    constructor(filepath) {
        this.filepath = filepath;
        this.doc = null;
        this.asset = null;
        this.load();
    }

    load() {
        const xmlText = fs.readFileSync(this.filepath, 'utf-8');
        const parser = new DOMParser();
        this.doc = parser.parseFromString(xmlText);
        this.asset = this.doc.getElementsByTagName('asset')[0];
    }

    printProperties() {
        if (!this.asset) {
            console.log('No <asset> element found.');
            return;
        }

        console.log('DAE Properties:');

        // Contributors
        const contributors = this.asset.getElementsByTagName('contributor');
        for (let i = 0; i < contributors.length; i++) {
            const contrib = contributors[i];
            console.log(`\nContributor ${i + 1}:`);
            console.log(`  Author: ${this.getText(contrib, 'author')}`);
            console.log(`  Author Email: ${this.getText(contrib, 'author_email')}`);
            console.log(`  Author Website: ${this.getText(contrib, 'author_website')}`);
            console.log(`  Authoring Tool: ${this.getText(contrib, 'authoring_tool')}`);
            console.log(`  Comments: ${this.getText(contrib, 'comments')}`);
            console.log(`  Copyright: ${this.getText(contrib, 'copyright')}`);
            console.log(`  Source Data: ${this.getText(contrib, 'source_data')}`);
        }

        // Geographic Location
        const geo = this.asset.getElementsByTagName('geographic_location')[0];
        if (geo) {
            console.log('\nGeographic Location:');
            console.log(`  Longitude: ${this.getText(geo, 'longitude')}`);
            console.log(`  Latitude: ${this.getText(geo, 'latitude')}`);
            const alt = geo.getElementsByTagName('altitude')[0];
            console.log(`  Altitude: ${alt ? alt.textContent : 'N/A'} (Mode: ${alt ? alt.getAttribute('mode') : 'N/A'})`);
        }

        // Other properties
        console.log(`\nCreated: ${this.getText(this.asset, 'created')}`);
        console.log(`Keywords: ${this.getText(this.asset, 'keywords')}`);
        console.log(`Modified: ${this.getText(this.asset, 'modified')}`);
        console.log(`Revision: ${this.getText(this.asset, 'revision')}`);
        console.log(`Subject: ${this.getText(this.asset, 'subject')}`);
        console.log(`Title: ${this.getText(this.asset, 'title')}`);

        // Unit
        const unit = this.asset.getElementsByTagName('unit')[0];
        console.log(`Unit: Name=${unit ? unit.getAttribute('name') : 'N/A'}, Meter=${unit ? unit.getAttribute('meter') : 'N/A'}`);

        // Up Axis
        console.log(`Up Axis: ${this.getText(this.asset, 'up_axis')}`);
    }

    getText(parent, tag) {
        const elem = parent.getElementsByTagName(tag)[0];
        return elem ? elem.textContent : 'N/A';
    }

    updateAndWrite(newFilepath, key, value) {
        if (!this.asset) {
            console.log('No <asset> to update.');
            return;
        }
        const elem = this.asset.getElementsByTagName(key)[0];
        if (elem) {
            elem.textContent = value;
        }
        const serializer = new XMLSerializer();
        const xmlStr = serializer.serializeToString(this.doc);
        fs.writeFileSync(newFilepath, xmlStr);
    }
}

// Usage example (Node.js):
// const processor = new DAEProcessor('example.dae');
// processor.printProperties();
// processor.updateAndWrite('modified.dae', 'modified', new Date().toISOString());

C Class for .DAE Properties

Note: Standard C lacks built-in XML parsing, so this is implemented in C++ for practicality (using <fstream>, <string>, and simple string parsing for extraction, as a full XML parser like libxml2 is not assumed). It opens the file, reads the content, extracts properties via string searches (approximate for simplicity; not robust for all XML variations), prints to console, and includes a write method to modify and save.

#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <ctime> // For time in write

class DAEProcessor {
private:
    std::string filepath;
    std::string xmlContent;
    std::string extractTag(const std::string& tag) {
        size_t start = xmlContent.find("<" + tag + ">") ;
        if (start == std::string::npos) return "N/A";
        start += tag.length() + 2;
        size_t end = xmlContent.find("</" + tag + ">", start);
        if (end == std::string::npos) return "N/A";
        return xmlContent.substr(start, end - start);
    }

    std::vector<std::string> extractContributors(const std::string& subTag) {
        std::vector<std::string> results;
        size_t pos = 0;
        while ((pos = xmlContent.find("<contributor>", pos)) != std::string::npos) {
            size_t subStart = xmlContent.find("<" + subTag + ">", pos);
            if (subStart != std::string::npos) {
                subStart += subTag.length() + 2;
                size_t subEnd = xmlContent.find("</" + subTag + ">", subStart);
                if (subEnd != std::string::npos) {
                    results.push_back(xmlContent.substr(subStart, subEnd - subStart));
                }
            }
            pos += 13; // Skip to next
        }
        return results;
    }

public:
    DAEProcessor(const std::string& fp) : filepath(fp) {
        std::ifstream file(fp);
        if (file) {
            xmlContent.assign((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
        }
    }

    void printProperties() {
        if (xmlContent.empty()) {
            std::cout << "Failed to load file." << std::endl;
            return;
        }
        if (xmlContent.find("<asset>") == std::string::npos) {
            std::cout << "No <asset> element found." << std::endl;
            return;
        }

        std::cout << "DAE Properties:" << std::endl;

        // Contributors (example for author and authoring_tool; extend similarly)
        auto authors = extractContributors("author");
        auto tools = extractContributors("authoring_tool");
        // ... (similar for other sub-tags)
        for (size_t i = 0; i < authors.size(); ++i) {
            std::cout << "\nContributor " << (i + 1) << ":" << std::endl;
            std::cout << "  Author: " << (i < authors.size() ? authors[i] : "N/A") << std::endl;
            std::cout << "  Authoring Tool: " << (i < tools.size() ? tools[i] : "N/A") << std::endl;
            // Add more sub-properties similarly
        }

        // Geographic (simplified; assume single)
        std::cout << "\nGeographic Location:" << std::endl;
        std::cout << "  Longitude: " << extractTag("longitude") << std::endl;
        std::cout << "  Latitude: " << extractTag("latitude") << std::endl;
        std::cout << "  Altitude: " << extractTag("altitude") << std::endl; // Mode not extracted for simplicity

        // Other properties
        std::cout << "\nCreated: " << extractTag("created") << std::endl;
        std::cout << "Keywords: " << extractTag("keywords") << std::endl;
        std::cout << "Modified: " << extractTag("modified") << std::endl;
        std::cout << "Revision: " << extractTag("revision") << std::endl;
        std::cout << "Subject: " << extractTag("subject") << std::endl;
        std::cout << "Title: " << extractTag("title") << std::endl;

        // Unit (extract attributes manually)
        size_t unitPos = xmlContent.find("<unit ");
        std::string unitName = "N/A", unitMeter = "N/A";
        if (unitPos != std::string::npos) {
            size_t nameStart = xmlContent.find("name=\"", unitPos);
            if (nameStart != std::string::npos) {
                nameStart += 6;
                size_t nameEnd = xmlContent.find("\"", nameStart);
                unitName = xmlContent.substr(nameStart, nameEnd - nameStart);
            }
            size_t meterStart = xmlContent.find("meter=\"", unitPos);
            if (meterStart != std::string::npos) {
                meterStart += 7;
                size_t meterEnd = xmlContent.find("\"", meterStart);
                unitMeter = xmlContent.substr(meterStart, meterEnd - meterStart);
            }
        }
        std::cout << "Unit: Name=" << unitName << ", Meter=" << unitMeter << std::endl;

        std::cout << "Up Axis: " << extractTag("up_axis") << std::endl;
    }

    void updateAndWrite(const std::string& newFilepath, const std::string& key, const std::string& value) {
        if (xmlContent.empty()) return;
        size_t start = xmlContent.find("<" + key + ">");
        if (start != std::string::npos) {
            size_t end = xmlContent.find("</" + key + ">", start);
            if (end != std::string::npos) {
                xmlContent.replace(start + key.length() + 2, end - (start + key.length() + 2), value);
            }
        }
        std::ofstream out(newFilepath);
        out << xmlContent;
    }
};

// Usage example:
// int main() {
//     DAEProcessor processor("example.dae");
//     processor.printProperties();
//     time_t now = time(0);
//     char buf[25];
//     strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%SZ", gmtime(&now));
//     processor.updateAndWrite("modified.dae", "modified", buf);
//     return 0;
// }