== blender file format ==
Hello, from the bconf 2010 from Jeroen and Luca. Our first combined commit :) Automatically create sdna documentations from Trunk. Usage: blender2.5 -b -P BlendFileDnaExporter_25.py [-- [options]] Options: --dna-keep-blend: doesn't delete the produced blend file DNA export to html --dna-debug: sets the logging level to DEBUG (lots of additional info) --dna-versioned' saves version informations in the html and blend filenames --dna-overwrite-css' overwrite dna.css, useful when modifying css in the script Examples: default: % blender2.5 -b -P BlendFileDnaExporter_25.py with options: % blender2.5 -b -P BlendFileDnaExporter_25.py -- --dna-keep-blend --dna-debug
This commit is contained in:
477
doc/blender_file_format/BlendFileDnaExporter_25.py
Executable file
477
doc/blender_file_format/BlendFileDnaExporter_25.py
Executable file
@@ -0,0 +1,477 @@
|
||||
#! /usr/bin/env python3
|
||||
|
||||
# ***** BEGIN GPL LICENSE BLOCK *****
|
||||
#
|
||||
# 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
#
|
||||
# ***** END GPL LICENCE BLOCK *****
|
||||
|
||||
######################################################
|
||||
#
|
||||
# Name:
|
||||
# dna.py
|
||||
#
|
||||
# Description:
|
||||
# Creates a browsable DNA output to HTML.
|
||||
#
|
||||
# Author:
|
||||
# Jeroen Bakker
|
||||
#
|
||||
# Version:
|
||||
# v0.1 (12-05-2009) - migration of original source code to python.
|
||||
# Added code to support blender 2.5 branch
|
||||
# v0.2 (25-05-2009) - integrated with BlendFileReader.py
|
||||
#
|
||||
# Input:
|
||||
# blender build executable
|
||||
#
|
||||
# Output:
|
||||
# dna.html
|
||||
# dna.css (will only be created when not existing)
|
||||
#
|
||||
# Startup:
|
||||
# ./blender -P BlendFileDnaExporter.py
|
||||
#
|
||||
# Process:
|
||||
# 1: write blend file with SDNA info
|
||||
# 2: read blend header from blend file
|
||||
# 3: seek DNA1 file-block
|
||||
# 4: read dna record from blend file
|
||||
# 5: close and eventually delete temp blend file
|
||||
# 6: export dna to html and css
|
||||
# 7: quit blender
|
||||
#
|
||||
######################################################
|
||||
|
||||
import struct
|
||||
import sys
|
||||
import getopt # command line arguments handling
|
||||
from string import Template # strings completion
|
||||
|
||||
|
||||
# logs
|
||||
import logging
|
||||
log = logging.getLogger("BlendFileDnaExporter")
|
||||
|
||||
if '--dna-debug' in sys.argv:
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
else:
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
|
||||
class DNACatalogHTML:
|
||||
'''
|
||||
DNACatalog is a catalog of all information in the DNA1 file-block
|
||||
'''
|
||||
|
||||
def __init__(self, catalog, bpy_module = None):
|
||||
self.Catalog = catalog
|
||||
self.bpy = bpy_module
|
||||
|
||||
def WriteToHTML(self, handle):
|
||||
|
||||
dna_html_template = """
|
||||
<!DOCTYPE html PUBLIC -//W3C//DTD HTML 4.01 Transitional//EN http://www.w3.org/TR/html4/loose.dtd>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" type="text/css" href="dna.css" media="screen, print" />
|
||||
<meta http-equiv="Content-Type" content="text/html"; charset="ISO-8859-1" />
|
||||
<title>The mystery of the blend</title>
|
||||
</head>
|
||||
<body>
|
||||
<div class=title>
|
||||
Blender ${version}<br/>
|
||||
Internal SDNA structures
|
||||
</div>
|
||||
Architecture: ${bitness} ${endianness}<br/>
|
||||
Build revision: <a href="https://svn.blender.org/svnroot/bf-blender/!svn/bc/${revision}/trunk/">${revision}</a><br/>
|
||||
File format reference: <a href="mystery_of_the_blend.html">The mystery of the blend</a> by Jeroen Bakker<br/>
|
||||
<h1>Index of blender structures</h1>
|
||||
<ul class=multicolumn>
|
||||
${structs_list}
|
||||
</ul>
|
||||
${structs_content}
|
||||
</body>
|
||||
</html>"""
|
||||
|
||||
header = self.Catalog.Header
|
||||
bpy = self.bpy
|
||||
|
||||
# ${version} and ${revision}
|
||||
if bpy:
|
||||
version = '.'.join(map(str, bpy.app.version))
|
||||
revision = bpy.app.build_revision[:-1]
|
||||
else:
|
||||
version = str(header.Version)
|
||||
revision = 'Unknown'
|
||||
|
||||
# ${bitness}
|
||||
if header.PointerSize == 8:
|
||||
bitness = '64 bit'
|
||||
else:
|
||||
bitness = '32 bit'
|
||||
|
||||
# ${endianness}
|
||||
if header.LittleEndianness:
|
||||
endianess= 'Little endianness'
|
||||
else:
|
||||
endianess= 'Big endianness'
|
||||
|
||||
# ${structs_list}
|
||||
log.debug("Creating structs index")
|
||||
structs_list = ''
|
||||
list_item = '<li class="multicolumn">({0}) <a href="#{1}">{1}</a></li>\n'
|
||||
structureIndex = 0
|
||||
for structure in self.Catalog.Structs:
|
||||
structs_list += list_item.format(structureIndex, structure.Type.Name)
|
||||
structureIndex+=1
|
||||
|
||||
# ${structs_content}
|
||||
log.debug("Creating structs content")
|
||||
structs_content = ''
|
||||
for structure in self.Catalog.Structs:
|
||||
log.debug(structure.Type.Name)
|
||||
structs_content += self.Structure(structure)
|
||||
|
||||
d = dict(
|
||||
version = version,
|
||||
revision = revision,
|
||||
bitness = bitness,
|
||||
endianness = endianess,
|
||||
structs_list = structs_list,
|
||||
structs_content = structs_content
|
||||
)
|
||||
|
||||
dna_html = Template(dna_html_template).substitute(d)
|
||||
dna_html = self.format(dna_html)
|
||||
handle.write(dna_html)
|
||||
|
||||
def Structure(self, structure):
|
||||
struct_table_template = """
|
||||
<table><a name="${struct_name}"></a>
|
||||
<caption><a href="#${struct_name}">${struct_name}</a></caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>reference</th>
|
||||
<th>structure</th>
|
||||
<th>type</th>
|
||||
<th>name</th>
|
||||
<th>offset</th>
|
||||
<th>size</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${fields}
|
||||
</tbody>
|
||||
</table>
|
||||
<label>Total size: ${size} bytes</label><br/>
|
||||
<label>(<a href="#top">top</a>)</label><br/>"""
|
||||
|
||||
d = dict(
|
||||
struct_name = structure.Type.Name,
|
||||
fields = self.StructureFields(structure, None, 0),
|
||||
size = str(structure.Type.Size)
|
||||
)
|
||||
|
||||
struct_table = Template(struct_table_template).substitute(d)
|
||||
return struct_table
|
||||
|
||||
def StructureFields(self, structure, parentReference, offset):
|
||||
fields = ''
|
||||
for field in structure.Fields:
|
||||
fields += self.StructureField(field, structure, parentReference, offset)
|
||||
offset += field.Size(self.Catalog.Header)
|
||||
return fields
|
||||
|
||||
def StructureField(self, field, structure, parentReference, offset):
|
||||
structure_field_template = """
|
||||
<tr>
|
||||
<td>${reference}</td>
|
||||
<td>${struct}</td>
|
||||
<td>${type}</td>
|
||||
<td>${name}</td>
|
||||
<td>${offset}</td>
|
||||
<td>${size}</td>
|
||||
</tr>"""
|
||||
|
||||
if field.Type.Structure == None or field.Name.IsPointer():
|
||||
|
||||
# ${reference}
|
||||
reference = field.Name.AsReference(parentReference)
|
||||
|
||||
# ${struct}
|
||||
if parentReference != None:
|
||||
struct = '<a href="#{0}">{0}</a>'.format(structure.Type.Name)
|
||||
else:
|
||||
struct = structure.Type.Name
|
||||
|
||||
# ${type}
|
||||
type = field.Type.Name
|
||||
|
||||
# ${name}
|
||||
name = field.Name.Name
|
||||
|
||||
# ${offset}
|
||||
# offset already set
|
||||
|
||||
# ${size}
|
||||
size = field.Size(self.Catalog.Header)
|
||||
|
||||
d = dict(
|
||||
reference = reference,
|
||||
struct = struct,
|
||||
type = type,
|
||||
name = name,
|
||||
offset = offset,
|
||||
size = size
|
||||
)
|
||||
|
||||
structure_field = Template(structure_field_template).substitute(d)
|
||||
|
||||
elif field.Type.Structure != None:
|
||||
reference = field.Name.AsReference(parentReference)
|
||||
structure_field = self.StructureFields(field.Type.Structure, reference, offset)
|
||||
|
||||
return structure_field
|
||||
|
||||
def indent(self, input, dent, startswith = ''):
|
||||
output = ''
|
||||
if dent < 0:
|
||||
for line in input.split('\n'):
|
||||
dent = abs(dent)
|
||||
output += line[dent:] + '\n' # unindent of a desired amount
|
||||
elif dent == 0:
|
||||
for line in input.split('\n'):
|
||||
output += line.lstrip() + '\n' # remove indentation completely
|
||||
elif dent > 0:
|
||||
for line in input.split('\n'):
|
||||
output += ' '* dent + line + '\n'
|
||||
return output
|
||||
|
||||
def format(self, input):
|
||||
diff = {
|
||||
'\n<!DOCTYPE':'<!DOCTYPE',
|
||||
'\n</ul>' :'</ul>',
|
||||
'<a name' :'\n<a name',
|
||||
'<tr>\n' :'<tr>',
|
||||
'<tr>' :' <tr>',
|
||||
'</th>\n' :'</th>',
|
||||
'</td>\n' :'</td>',
|
||||
'<tbody>\n' :'<tbody>'
|
||||
}
|
||||
output = self.indent(input, 0)
|
||||
for key, value in diff.items():
|
||||
output = output.replace(key, value)
|
||||
return output
|
||||
|
||||
def WriteToCSS(self, handle):
|
||||
'''
|
||||
Write the Cascading stylesheet template to the handle
|
||||
It is expected that the handle is a Filehandle
|
||||
'''
|
||||
css = """
|
||||
@CHARSET "ISO-8859-1";
|
||||
|
||||
body {
|
||||
font-family: verdana;
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
div.title {
|
||||
font-size: large;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
h1 {
|
||||
page-break-before: always;
|
||||
}
|
||||
|
||||
h1, h2 {
|
||||
background-color: #D3D3D3;
|
||||
color:#404040;
|
||||
margin-right: 3%;
|
||||
padding-left: 40px;
|
||||
}
|
||||
|
||||
h1:hover{
|
||||
background-color: #EBEBEB;
|
||||
}
|
||||
|
||||
h3 {
|
||||
padding-left: 40px;
|
||||
}
|
||||
|
||||
table {
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: #000000;
|
||||
border-collapse: collapse;
|
||||
width: 94%;
|
||||
margin: 20px 3% 10px;
|
||||
}
|
||||
|
||||
caption {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
th {
|
||||
background-color: #000000;
|
||||
color:#ffffff;
|
||||
padding-left:5px;
|
||||
padding-right:5px;
|
||||
}
|
||||
|
||||
tr {
|
||||
}
|
||||
|
||||
td {
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: #a0a0a0;
|
||||
padding-left:5px;
|
||||
padding-right:5px;
|
||||
}
|
||||
|
||||
label {
|
||||
float:right;
|
||||
margin-right: 3%;
|
||||
}
|
||||
|
||||
ul.multicolumn {
|
||||
list-style:none;
|
||||
float:left;
|
||||
padding-right:0px;
|
||||
margin-right:0px;
|
||||
}
|
||||
|
||||
li.multicolumn {
|
||||
float:left;
|
||||
width:200px;
|
||||
margin-right:0px;
|
||||
}
|
||||
|
||||
a {
|
||||
color:#a000a0;
|
||||
text-decoration:none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color:#a000a0;
|
||||
text-decoration:underline;
|
||||
}
|
||||
"""
|
||||
|
||||
css = self.indent(css, 0)
|
||||
|
||||
handle.write(css)
|
||||
|
||||
|
||||
def usage():
|
||||
print("\nUsage: \n\tblender2.5 -b -P BlendFileDnaExporter_25.py [-- [options]]")
|
||||
print("Options:")
|
||||
print("\t--dna-keep-blend: doesn't delete the produced blend file DNA export to html")
|
||||
print("\t--dna-debug: sets the logging level to DEBUG (lots of additional info)")
|
||||
print("\t--dna-versioned' saves version informations in the html and blend filenames")
|
||||
print("\t--dna-overwrite-css' overwrite dna.css, useful when modifying css in the script")
|
||||
print("Examples:")
|
||||
print("\tdefault: % blender2.5 -b -P BlendFileDnaExporter_25.py")
|
||||
print("\twith options: % blender2.5 -b -P BlendFileDnaExporter_25.py -- --dna-keep-blend --dna-debug\n")
|
||||
|
||||
|
||||
######################################################
|
||||
# Main
|
||||
######################################################
|
||||
|
||||
def main():
|
||||
|
||||
import os, os.path
|
||||
|
||||
try:
|
||||
bpy = __import__('bpy')
|
||||
|
||||
# Files
|
||||
if '--dna-versioned' in sys.argv:
|
||||
blender_version = '_'.join(map(str, bpy.app.version))
|
||||
filename = 'dna-{0}-{1}_endian-{2}-r{3}'.format(sys.arch, sys.byteorder, blender_version, bpy.app.build_revision[2:-1])
|
||||
else:
|
||||
filename = 'dna'
|
||||
dir = os.path.dirname(__file__)
|
||||
Path_Blend = os.path.join(dir, filename + '.blend') # temporary blend file
|
||||
Path_HTML = os.path.join(dir, filename + '.html') # output html file
|
||||
Path_CSS = os.path.join(dir, 'dna.css') # output css file
|
||||
|
||||
# create a blend file for dna parsing
|
||||
if not os.path.exists(Path_Blend):
|
||||
log.info("1: write temp blend file with SDNA info")
|
||||
log.info(" saving to: " + Path_Blend)
|
||||
try:
|
||||
bpy.ops.wm.save_as_mainfile(filepath = Path_Blend, copy = True, compress = False)
|
||||
except:
|
||||
log.error("Filename {0} does not exist and can't be created... quitting".format(Path_Blend))
|
||||
return
|
||||
else:
|
||||
log.info("1: found blend file with SDNA info")
|
||||
log.info(" " + Path_Blend)
|
||||
|
||||
# read blend header from blend file
|
||||
log.info("2: read file:")
|
||||
|
||||
if not dir in sys.path:
|
||||
sys.path.append(dir)
|
||||
import BlendFileReader
|
||||
|
||||
handle = BlendFileReader.openBlendFile(Path_Blend)
|
||||
blendfile = BlendFileReader.BlendFile(handle)
|
||||
catalog = DNACatalogHTML(blendfile.Catalog, bpy)
|
||||
|
||||
# close temp file
|
||||
handle.close()
|
||||
|
||||
# deleting or not?
|
||||
if '--dna-keep-blend' in sys.argv:
|
||||
# keep the blend, useful for studying hexdumps
|
||||
log.info("5: closing blend file:")
|
||||
log.info(" {0}".format(Path_Blend))
|
||||
else:
|
||||
# delete the blend
|
||||
log.info("5: close and delete temp blend:")
|
||||
log.info(" {0}".format(Path_Blend))
|
||||
os.remove(Path_Blend)
|
||||
|
||||
# export dna to xhtml
|
||||
log.info("6: export sdna to xhtml file")
|
||||
handleHTML = open(Path_HTML, "w")
|
||||
catalog.WriteToHTML(handleHTML)
|
||||
handleHTML.close()
|
||||
|
||||
# only write the css when doesn't exist or at explicit request
|
||||
if not os.path.exists(Path_CSS) or '--dna-overwrite-css' in sys.argv:
|
||||
handleCSS = open(Path_CSS, "w")
|
||||
catalog.WriteToCSS(handleCSS)
|
||||
handleCSS.close()
|
||||
|
||||
# quit blender
|
||||
if not bpy.app.background:
|
||||
log.info("7: quit blender")
|
||||
bpy.ops.wm.exit_blender()
|
||||
|
||||
except ImportError:
|
||||
log.warning(" skipping, not running in Blender")
|
||||
usage()
|
||||
sys.exit(2)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
446
doc/blender_file_format/BlendFileReader.py
Normal file
446
doc/blender_file_format/BlendFileReader.py
Normal file
@@ -0,0 +1,446 @@
|
||||
#! /usr/bin/env python3
|
||||
|
||||
# ***** BEGIN GPL LICENSE BLOCK *****
|
||||
#
|
||||
# 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
#
|
||||
# ***** END GPL LICENCE BLOCK *****
|
||||
|
||||
######################################################
|
||||
# Importing modules
|
||||
######################################################
|
||||
|
||||
import os
|
||||
import struct
|
||||
import gzip
|
||||
import tempfile
|
||||
|
||||
import logging
|
||||
log = logging.getLogger("BlendFileReader")
|
||||
|
||||
######################################################
|
||||
# module global routines
|
||||
######################################################
|
||||
|
||||
def ReadString(handle, length):
|
||||
'''
|
||||
ReadString reads a String of given length or a zero terminating String
|
||||
from a file handle
|
||||
'''
|
||||
if length != 0:
|
||||
return handle.read(length).decode()
|
||||
else:
|
||||
# length == 0 means we want a zero terminating string
|
||||
result = ""
|
||||
s = ReadString(handle, 1)
|
||||
while s!="\0":
|
||||
result += s
|
||||
s = ReadString(handle, 1)
|
||||
return result
|
||||
|
||||
|
||||
def Read(type, handle, fileheader):
|
||||
'''
|
||||
Reads the chosen type from a file handle
|
||||
'''
|
||||
def unpacked_bytes(type_char, size):
|
||||
return struct.unpack(fileheader.StructPre + type_char, handle.read(size))[0]
|
||||
|
||||
if type == 'ushort':
|
||||
return unpacked_bytes("H", 2) # unsigned short
|
||||
elif type == 'short':
|
||||
return unpacked_bytes("h", 2) # short
|
||||
elif type == 'uint':
|
||||
return unpacked_bytes("I", 4) # unsigned int
|
||||
elif type == 'int':
|
||||
return unpacked_bytes("i", 4) # int
|
||||
elif type == 'float':
|
||||
return unpacked_bytes("f", 4) # float
|
||||
elif type == 'ulong':
|
||||
return unpacked_bytes("Q", 8) # unsigned long
|
||||
elif type == 'pointer':
|
||||
# The pointersize is given by the header (BlendFileHeader).
|
||||
if fileheader.PointerSize == 4:
|
||||
return Read('uint', handle, fileheader)
|
||||
if fileheader.PointerSize == 8:
|
||||
return Read('ulong', handle, fileheader)
|
||||
|
||||
|
||||
def openBlendFile(filename):
|
||||
'''
|
||||
Open a filename, determine if the file is compressed and returns a handle
|
||||
'''
|
||||
handle = open(filename, 'rb')
|
||||
magic = ReadString(handle, 7)
|
||||
if magic in ("BLENDER", "BULLETf"):
|
||||
log.debug("normal blendfile detected")
|
||||
handle.seek(0, os.SEEK_SET)
|
||||
return handle
|
||||
else:
|
||||
log.debug("gzip blendfile detected?")
|
||||
handle.close()
|
||||
log.debug("decompressing started")
|
||||
fs = gzip.open(filename, "rb")
|
||||
handle = tempfile.TemporaryFile()
|
||||
data = fs.read(1024*1024)
|
||||
while data:
|
||||
handle.write(data)
|
||||
data = fs.read(1024*1024)
|
||||
log.debug("decompressing finished")
|
||||
fs.close()
|
||||
log.debug("resetting decompressed file")
|
||||
handle.seek(0, os.SEEK_SET)
|
||||
return handle
|
||||
|
||||
|
||||
def Align(handle):
|
||||
'''
|
||||
Aligns the filehandle on 4 bytes
|
||||
'''
|
||||
offset = handle.tell()
|
||||
trim = offset % 4
|
||||
if trim != 0:
|
||||
handle.seek(4-trim, os.SEEK_CUR)
|
||||
|
||||
|
||||
######################################################
|
||||
# module classes
|
||||
######################################################
|
||||
|
||||
class BlendFile:
|
||||
'''
|
||||
Reads a blendfile and store the header, all the fileblocks, and catalogue
|
||||
structs foound in the DNA fileblock
|
||||
|
||||
- BlendFile.Header (BlendFileHeader instance)
|
||||
- BlendFile.Blocks (list of BlendFileBlock instances)
|
||||
- BlendFile.Catalog (DNACatalog instance)
|
||||
'''
|
||||
|
||||
def __init__(self, handle):
|
||||
log.debug("initializing reading blend-file")
|
||||
self.Header = BlendFileHeader(handle)
|
||||
self.Blocks = []
|
||||
fileblock = BlendFileBlock(handle, self)
|
||||
found_dna_block = False
|
||||
while not found_dna_block:
|
||||
if fileblock.Header.Code in ("DNA1", "SDNA"):
|
||||
self.Catalog = DNACatalog(self.Header, handle)
|
||||
found_dna_block = True
|
||||
else:
|
||||
fileblock.Header.skip(handle)
|
||||
|
||||
self.Blocks.append(fileblock)
|
||||
fileblock = BlendFileBlock(handle, self)
|
||||
|
||||
# appending last fileblock, "ENDB"
|
||||
self.Blocks.append(fileblock)
|
||||
|
||||
# seems unused?
|
||||
"""
|
||||
def FindBlendFileBlocksWithCode(self, code):
|
||||
#result = []
|
||||
#for block in self.Blocks:
|
||||
#if block.Header.Code.startswith(code) or block.Header.Code.endswith(code):
|
||||
#result.append(block)
|
||||
#return result
|
||||
"""
|
||||
|
||||
|
||||
class BlendFileHeader:
|
||||
'''
|
||||
BlendFileHeader allocates the first 12 bytes of a blend file.
|
||||
It contains information about the hardware architecture.
|
||||
Header example: BLENDER_v254
|
||||
|
||||
BlendFileHeader.Magic (str)
|
||||
BlendFileHeader.PointerSize (int)
|
||||
BlendFileHeader.LittleEndianness (bool)
|
||||
BlendFileHeader.StructPre (str) see http://docs.python.org/py3k/library/struct.html#byte-order-size-and-alignment
|
||||
BlendFileHeader.Version (int)
|
||||
'''
|
||||
|
||||
def __init__(self, handle):
|
||||
log.debug("reading blend-file-header")
|
||||
|
||||
self.Magic = ReadString(handle, 7)
|
||||
log.debug(self.Magic)
|
||||
|
||||
pointersize = ReadString(handle, 1)
|
||||
log.debug(pointersize)
|
||||
if pointersize == "-":
|
||||
self.PointerSize = 8
|
||||
if pointersize == "_":
|
||||
self.PointerSize = 4
|
||||
|
||||
endianness = ReadString(handle, 1)
|
||||
log.debug(endianness)
|
||||
if endianness == "v":
|
||||
self.LittleEndianness = True
|
||||
self.StructPre = "<"
|
||||
if endianness == "V":
|
||||
self.LittleEndianness = False
|
||||
self.StructPre = ">"
|
||||
|
||||
version = ReadString(handle, 3)
|
||||
log.debug(version)
|
||||
self.Version = int(version)
|
||||
|
||||
log.debug("{0} {1} {2} {3}".format(self.Magic, self.PointerSize, self.LittleEndianness, version))
|
||||
|
||||
|
||||
class BlendFileBlock:
|
||||
'''
|
||||
BlendFileBlock.File (BlendFile)
|
||||
BlendFileBlock.Header (FileBlockHeader)
|
||||
'''
|
||||
|
||||
def __init__(self, handle, blendfile):
|
||||
self.File = blendfile
|
||||
self.Header = FileBlockHeader(handle, blendfile.Header)
|
||||
|
||||
def Get(self, handle, path):
|
||||
log.debug("find dna structure")
|
||||
dnaIndex = self.Header.SDNAIndex
|
||||
dnaStruct = self.File.Catalog.Structs[dnaIndex]
|
||||
log.debug("found " + dnaStruct.Type.Name)
|
||||
handle.seek(self.Header.FileOffset, os.SEEK_SET)
|
||||
return dnaStruct.GetField(self.File.Header, handle, path)
|
||||
|
||||
|
||||
class FileBlockHeader:
|
||||
'''
|
||||
FileBlockHeader contains the information in a file-block-header.
|
||||
The class is needed for searching to the correct file-block (containing Code: DNA1)
|
||||
|
||||
Code (str)
|
||||
Size (int)
|
||||
OldAddress (pointer)
|
||||
SDNAIndex (int)
|
||||
Count (int)
|
||||
FileOffset (= file pointer of datablock)
|
||||
'''
|
||||
|
||||
def __init__(self, handle, fileheader):
|
||||
self.Code = ReadString(handle, 4).strip()
|
||||
if self.Code != "ENDB":
|
||||
self.Size = Read('uint', handle, fileheader)
|
||||
self.OldAddress = Read('pointer', handle, fileheader)
|
||||
self.SDNAIndex = Read('uint', handle, fileheader)
|
||||
self.Count = Read('uint', handle, fileheader)
|
||||
self.FileOffset = handle.tell()
|
||||
else:
|
||||
self.Size = Read('uint', handle, fileheader)
|
||||
self.OldAddress = 0
|
||||
self.SDNAIndex = 0
|
||||
self.Count = 0
|
||||
self.FileOffset = handle.tell()
|
||||
#self.Code += ' ' * (4 - len(self.Code))
|
||||
log.debug("found blend-file-block-fileheader {0} {1}".format(self.Code, self.FileOffset))
|
||||
|
||||
def skip(self, handle):
|
||||
handle.read(self.Size)
|
||||
|
||||
|
||||
class DNACatalog:
|
||||
'''
|
||||
DNACatalog is a catalog of all information in the DNA1 file-block
|
||||
|
||||
Header = None
|
||||
Names = None
|
||||
Types = None
|
||||
Structs = None
|
||||
'''
|
||||
|
||||
def __init__(self, fileheader, handle):
|
||||
log.debug("building DNA catalog")
|
||||
self.Names=[]
|
||||
self.Types=[]
|
||||
self.Structs=[]
|
||||
self.Header = fileheader
|
||||
|
||||
SDNA = ReadString(handle, 4)
|
||||
|
||||
# names
|
||||
NAME = ReadString(handle, 4)
|
||||
numberOfNames = Read('uint', handle, fileheader)
|
||||
log.debug("building #{0} names".format(numberOfNames))
|
||||
for i in range(numberOfNames):
|
||||
name = ReadString(handle,0)
|
||||
self.Names.append(DNAName(name))
|
||||
Align(handle)
|
||||
|
||||
# types
|
||||
TYPE = ReadString(handle, 4)
|
||||
numberOfTypes = Read('uint', handle, fileheader)
|
||||
log.debug("building #{0} types".format(numberOfTypes))
|
||||
for i in range(numberOfTypes):
|
||||
type = ReadString(handle,0)
|
||||
self.Types.append(DNAType(type))
|
||||
Align(handle)
|
||||
|
||||
# type lengths
|
||||
TLEN = ReadString(handle, 4)
|
||||
log.debug("building #{0} type-lengths".format(numberOfTypes))
|
||||
for i in range(numberOfTypes):
|
||||
length = Read('ushort', handle, fileheader)
|
||||
self.Types[i].Size = length
|
||||
Align(handle)
|
||||
|
||||
# structs
|
||||
STRC = ReadString(handle, 4)
|
||||
numberOfStructures = Read('uint', handle, fileheader)
|
||||
log.debug("building #{0} structures".format(numberOfStructures))
|
||||
for structureIndex in range(numberOfStructures):
|
||||
type = Read('ushort', handle, fileheader)
|
||||
Type = self.Types[type]
|
||||
structure = DNAStructure(Type)
|
||||
self.Structs.append(structure)
|
||||
|
||||
numberOfFields = Read('ushort', handle, fileheader)
|
||||
for fieldIndex in range(numberOfFields):
|
||||
fTypeIndex = Read('ushort', handle, fileheader)
|
||||
fNameIndex = Read('ushort', handle, fileheader)
|
||||
fType = self.Types[fTypeIndex]
|
||||
fName = self.Names[fNameIndex]
|
||||
structure.Fields.append(DNAField(fType, fName))
|
||||
|
||||
|
||||
class DNAName:
|
||||
'''
|
||||
DNAName is a C-type name stored in the DNA.
|
||||
|
||||
Name = str
|
||||
'''
|
||||
|
||||
def __init__(self, name):
|
||||
self.Name = name
|
||||
|
||||
def AsReference(self, parent):
|
||||
if parent == None:
|
||||
result = ""
|
||||
else:
|
||||
result = parent+"."
|
||||
|
||||
result = result + self.ShortName()
|
||||
return result
|
||||
|
||||
def ShortName(self):
|
||||
result = self.Name;
|
||||
result = result.replace("*", "")
|
||||
result = result.replace("(", "")
|
||||
result = result.replace(")", "")
|
||||
Index = result.find("[")
|
||||
if Index != -1:
|
||||
result = result[0:Index]
|
||||
return result
|
||||
|
||||
def IsPointer(self):
|
||||
return self.Name.find("*")>-1
|
||||
|
||||
def IsMethodPointer(self):
|
||||
return self.Name.find("(*")>-1
|
||||
|
||||
def ArraySize(self):
|
||||
result = 1
|
||||
Temp = self.Name
|
||||
Index = Temp.find("[")
|
||||
|
||||
while Index != -1:
|
||||
Index2 = Temp.find("]")
|
||||
result*=int(Temp[Index+1:Index2])
|
||||
Temp = Temp[Index2+1:]
|
||||
Index = Temp.find("[")
|
||||
|
||||
return result
|
||||
|
||||
|
||||
class DNAType:
|
||||
'''
|
||||
DNAType is a C-type stored in the DNA
|
||||
|
||||
Name = str
|
||||
Size = int
|
||||
Structure = DNAStructure
|
||||
'''
|
||||
|
||||
def __init__(self, aName):
|
||||
self.Name = aName
|
||||
self.Structure=None
|
||||
|
||||
|
||||
class DNAStructure:
|
||||
'''
|
||||
DNAType is a C-type structure stored in the DNA
|
||||
|
||||
Type = DNAType
|
||||
Fields = [DNAField]
|
||||
'''
|
||||
|
||||
def __init__(self, aType):
|
||||
self.Type = aType
|
||||
self.Type.Structure = self
|
||||
self.Fields=[]
|
||||
|
||||
def GetField(self, header, handle, path):
|
||||
splitted = path.partition(".")
|
||||
name = splitted[0]
|
||||
rest = splitted[2]
|
||||
offset = 0;
|
||||
for field in self.Fields:
|
||||
if field.Name.ShortName() == name:
|
||||
log.debug("found "+name+"@"+str(offset))
|
||||
handle.seek(offset, os.SEEK_CUR)
|
||||
return field.DecodeField(header, handle, rest)
|
||||
else:
|
||||
offset += field.Size(header)
|
||||
|
||||
log.debug("error did not find "+path)
|
||||
return None
|
||||
|
||||
|
||||
class DNAField:
|
||||
'''
|
||||
DNAField is a coupled DNAType and DNAName.
|
||||
|
||||
Type = DNAType
|
||||
Name = DNAName
|
||||
'''
|
||||
|
||||
def __init__(self, aType, aName):
|
||||
self.Type = aType
|
||||
self.Name = aName
|
||||
|
||||
def Size(self, header):
|
||||
if self.Name.IsPointer() or self.Name.IsMethodPointer():
|
||||
return header.PointerSize*self.Name.ArraySize()
|
||||
else:
|
||||
return self.Type.Size*self.Name.ArraySize()
|
||||
|
||||
def DecodeField(self, header, handle, path):
|
||||
if path == "":
|
||||
if self.Name.IsPointer():
|
||||
return Read('pointer', handle, header)
|
||||
if self.Type.Name=="int":
|
||||
return Read('int', handle, header)
|
||||
if self.Type.Name=="short":
|
||||
return Read('short', handle, header)
|
||||
if self.Type.Name=="float":
|
||||
return Read('float', handle, header)
|
||||
if self.Type.Name=="char":
|
||||
return ReadString(handle, self.Name.ArraySize())
|
||||
else:
|
||||
return self.Type.Structure.GetField(header, handle, path)
|
||||
|
204
doc/blender_file_format/mystery_of_the_blend.css
Normal file
204
doc/blender_file_format/mystery_of_the_blend.css
Normal file
@@ -0,0 +1,204 @@
|
||||
@CHARSET "ISO-8859-1";
|
||||
|
||||
table {
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: #000000;
|
||||
border-collapse: collapse;
|
||||
width: 94%;
|
||||
margin: 10px 3%;
|
||||
}
|
||||
|
||||
DIV.title {
|
||||
font-size: 30px;
|
||||
font-weight: bold;
|
||||
text-align: center
|
||||
}
|
||||
|
||||
DIV.subtitle {
|
||||
font-size: large;
|
||||
text-align: center
|
||||
}
|
||||
|
||||
DIV.contact {
|
||||
margin:30px 3%;
|
||||
}
|
||||
|
||||
@media print {
|
||||
DIV.contact {
|
||||
margin-top: 300px;
|
||||
}
|
||||
DIV.title {
|
||||
margin-top: 400px;
|
||||
}
|
||||
}
|
||||
|
||||
label {
|
||||
font-weight: bold;
|
||||
width: 100px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
label:after {
|
||||
content: ":";
|
||||
}
|
||||
|
||||
TH {
|
||||
background-color: #000000;
|
||||
color: #ffffff;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
TR {
|
||||
}
|
||||
|
||||
TD {
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: #a0a0a0;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
BODY {
|
||||
font-family: verdana;
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
H1 {
|
||||
page-break-before: always;
|
||||
}
|
||||
|
||||
H1, H2, H3, H4 {
|
||||
margin-top: 30px;
|
||||
margin-right: 3%;
|
||||
padding: 3px 3%;
|
||||
color: #404040;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
H1, H2 {
|
||||
background-color: #D3D3D3;
|
||||
}
|
||||
|
||||
H3, H4 {
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
H1:hover, H2:hover, H3:hover, H4:hover {
|
||||
background-color: #EBEBEB;
|
||||
}
|
||||
|
||||
CODE.evidence {
|
||||
font-size:larger
|
||||
}
|
||||
|
||||
CODE.block {
|
||||
color: #000000;
|
||||
background-color: #DDDC75;
|
||||
margin: 10px 0;
|
||||
padding: 5px;
|
||||
border-width: 1px;
|
||||
border-style: dotted;
|
||||
border-color: #000000;
|
||||
white-space: pre;
|
||||
display: block;
|
||||
font-size: 2 em;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin: 10px 3%;
|
||||
}
|
||||
|
||||
li {
|
||||
margin: 0 -15px;
|
||||
}
|
||||
|
||||
ul.multicolumn {
|
||||
list-style: none;
|
||||
float: left;
|
||||
padding-right: 0px;
|
||||
margin-right: 0px;
|
||||
}
|
||||
|
||||
li.multicolumn {
|
||||
float: left;
|
||||
width: 200px;
|
||||
margin-right: 0px;
|
||||
}
|
||||
|
||||
@media screen {
|
||||
p {
|
||||
margin: 10px 3%;
|
||||
line-height: 130%;
|
||||
}
|
||||
}
|
||||
|
||||
span.fade {
|
||||
color: gray;
|
||||
}
|
||||
|
||||
span.header {
|
||||
color: green;
|
||||
}
|
||||
|
||||
span.header-greyed {
|
||||
color: #4CBE4B;
|
||||
}
|
||||
|
||||
span.data {
|
||||
color: blue;
|
||||
}
|
||||
|
||||
span.data-greyed {
|
||||
color: #5D99C4;
|
||||
}
|
||||
|
||||
span.descr {
|
||||
color: red;
|
||||
}
|
||||
|
||||
div.box {
|
||||
margin: 15px 3%;
|
||||
border-style: dotted;
|
||||
border-width: 1px;
|
||||
}
|
||||
|
||||
div.box-solid {
|
||||
margin: 15px 3%;
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
}
|
||||
|
||||
p.box-title {
|
||||
font-style: italic;
|
||||
font-size: 110%;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
p.box-title:hover {
|
||||
background-color: #EBEBEB;
|
||||
}
|
||||
|
||||
p.code {
|
||||
font-family: "Courier New", Courier, monospace;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #a000a0;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #a000a0;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
td.skip {
|
||||
color: #808080;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
text-align: center;
|
||||
}
|
835
doc/blender_file_format/mystery_of_the_blend.html
Normal file
835
doc/blender_file_format/mystery_of_the_blend.html
Normal file
@@ -0,0 +1,835 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" type="text/css" href="mystery_of_the_blend.css" media="screen, print">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
|
||||
<title>The mystery of the blend</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="title">The mystery of the blend</div>
|
||||
<div class="subtitle">The blender file-format explained</div>
|
||||
<div class="contact">
|
||||
<label>Author</label> Jeroen Bakker<br>
|
||||
<label>Email</label> <a href="mailto:j.bakker@atmind.nl">j.bakker@atmind.nl</a><br>
|
||||
<label>Website</label> <a href="http://www.atmind.nl/blender/">http://www.atmind.nl/blender</a><br>
|
||||
<label>Version</label> 06-10-2010<br>
|
||||
</div>
|
||||
|
||||
<a name="introduction" href="#introduction" ><h2>Introduction</h2></a>
|
||||
</a>
|
||||
|
||||
<p>In this article I will describe the
|
||||
blend-file-format with a request to tool-makers to support blend-file.
|
||||
|
||||
</p>
|
||||
<p>First I'll describe how Blender works with blend-files. You'll notice
|
||||
why the blend-file-format is not that well documented, as from
|
||||
Blender's perspective this is not needed.
|
||||
We look at the global file-structure of a blend-file (the file-header
|
||||
and file-blocks).
|
||||
After this is explained, we go deeper to the core of the blend-file, the
|
||||
DNA-structures. They hold the blue-prints of the blend-file and the key
|
||||
asset of understanding blend-files.
|
||||
When that's done we can use these DNA-structures to read information
|
||||
from elsewhere in the blend-file.
|
||||
|
||||
</p>
|
||||
<p>
|
||||
In this article we'll be using the default blend-file from Blender 2.54,
|
||||
with the goal to read the output resolution from the Scene.
|
||||
The article is written to be programming language independent and I've
|
||||
setup a web-site for support.
|
||||
</p>
|
||||
|
||||
<a name="loading-and-saving-in-blender" href="#loading-and-saving-in-blender">
|
||||
<h2>Loading and saving in Blender</h2>
|
||||
</a>
|
||||
|
||||
<p>
|
||||
Loading and saving in Blender is very fast and Blender is known to
|
||||
have excellent downward and upward compatibility. Ton Roosendaal
|
||||
demonstrated that in December 2008 by loading a 1.0 blend-file using
|
||||
Blender 2.48a [ref: <a href="http://www.blendernation.com/2008/12/01/blender-dna-rna-and-backward-compatibility/">http://www.blendernation.com/2008/12/01/blender-dna-rna-and-backward-compatibility/</a>].
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Saving complex scenes in Blender is done within seconds. Blender
|
||||
achieves this by saving data in memory to disk without any
|
||||
transformations or translations. Blender only adds file-block-headers to
|
||||
this data. A file-block-header contains clues on how to interpret the
|
||||
data. After the data, all internally Blender structures are stored.
|
||||
These structures will act as blue-prints when Blender loads the file.
|
||||
Blend-files can be different when stored on different hardware platforms
|
||||
or Blender releases. There is no effort taken to make blend-files
|
||||
binary the same. Blender creates the blend-files in this manner since
|
||||
release 1.0. Backward and upwards compatibility is not implemented when
|
||||
saving the file, this is done during loading.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
When Blender loads a blend-file, the DNA-structures are read first.
|
||||
Blender creates a catalog of these DNA-structures. Blender uses this
|
||||
catalog together with the data in the file, the internal Blender
|
||||
structures of the Blender release you're using and a lot of
|
||||
transformation and translation logic to implement the backward and
|
||||
upward compatibility. In the source code of blender there is actually
|
||||
logic which can transform and translate every structure used by a
|
||||
Blender release to the one of the release you're using [ref: <a href="http://download.blender.org/source/blender-2.48a.tar.gz">http://download.blender.org/source/blender-2.48a.tar.gz</a>
|
||||
<a href="https://svn.blender.org/svnroot/bf-blender/tags/blender-2.48-release/source/blender/blenloader/intern/readfile.c">blender/blenloader/intern/readfile.c</a> lines
|
||||
4946-7960]. The more difference between releases the more logic is
|
||||
executed.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The blend-file-format is not well documented, as it does not differ from
|
||||
internally used structures and the file can really explain itself.
|
||||
</p>
|
||||
|
||||
<a name="global-file-structure" href="#global-file-structure">
|
||||
<h2>Global file-structure</h2>
|
||||
</a>
|
||||
|
||||
<p>
|
||||
This section explains how the global file-structure can be read.
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>A blend-file always start with the <b>file-header</b></li>
|
||||
<li>After the file-header, follows a list of <b>file-blocks</b> (the default blend file of Blender 2.48 contains more than 400 of these file-blocks).</li>
|
||||
<li>Each file-block has a <b>file-block header</b> and <b>file-block data</b></li>
|
||||
<li>At the end of the blend-file there is a section called "<a href="#structure-DNA" style="font-weight:bold">Structure DNA</a>", which lists all the internal structures of the Blender release the file was created in</li>
|
||||
<li>The blend-file ends with a file-block called 'ENDB'</li>
|
||||
</ul>
|
||||
|
||||
<!-- file scheme -->
|
||||
<div class="box-solid" style="width:20%; margin-left:35%; font-size:0.8em;">
|
||||
|
||||
<p class="code"><b>File.blend</b></p>
|
||||
|
||||
<div class="box"><p class="code">File-header</p></div>
|
||||
|
||||
<div class="box-solid"><p class="code">File-block</p>
|
||||
<div class="box"><p class="code">Header</p></div>
|
||||
<div class="box"><p class="code">Data</p></div>
|
||||
</div>
|
||||
|
||||
<div class="box" style="border-style:dashed"><p class="code">File-block</p></div>
|
||||
<div class="box" style="border-style:dashed"><p class="code">File-block</p></div>
|
||||
|
||||
<div class="box-solid"><p class="code">File-block 'Structure DNA'</p>
|
||||
<div class="box"><p class="code">Header ('DNA1')</p></div>
|
||||
<div class="box-solid">
|
||||
<p class="code">Data ('SDNA')</p>
|
||||
<div class="box">
|
||||
<p class="code">Names ('NAME')</p>
|
||||
</div>
|
||||
<div class="box">
|
||||
<p class="code">Types ('TYPE')</p>
|
||||
</div>
|
||||
<div class="box">
|
||||
<p class="code">Lengths ('TLEN')</p>
|
||||
</div>
|
||||
<div class="box">
|
||||
<p class="code">Structures ('STRC')</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="box-solid"><p class="code">File-Block 'ENDB'</p></div>
|
||||
|
||||
</div><!-- end of file scheme -->
|
||||
|
||||
<a name="file-header" href="#file-header">
|
||||
<h3>File-Header</h3>
|
||||
</a>
|
||||
|
||||
<p>
|
||||
The first 12 bytes of every blend-file is the file-header. The
|
||||
file-header has information on Blender (version-number) and the PC the
|
||||
blend-file was saved on (pointer-size and endianness). This is required
|
||||
as all data inside the blend-file is ordered in that way, because no
|
||||
translation or transformation is done during saving.
|
||||
The next table describes the information in the file-header.
|
||||
</p>
|
||||
|
||||
<table>
|
||||
<caption>File-header</caption>
|
||||
<thead>
|
||||
<tr><th>reference</th>
|
||||
<th>structure</th>
|
||||
<th>type</th>
|
||||
<th>offset</th>
|
||||
<th>size</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><td>identifier</td>
|
||||
<td>char[7]</td>
|
||||
<td>File identifier (always 'BLENDER')</td>
|
||||
<td>0</td>
|
||||
<td>7</td></tr>
|
||||
<tr><td>pointer-size</td>
|
||||
<td>char</td>
|
||||
<td>Size of a pointer; all pointers in the file are stored in this format. '_' means 4 bytes or 32 bit and '-' means 8 bytes or 64 bits.</td>
|
||||
<td>7</td>
|
||||
<td>1</td></tr>
|
||||
<tr><td>endianness</td>
|
||||
<td>char</td>
|
||||
<td>Type of byte ordering used; 'v' means little endian and 'V' means big endian.</td>
|
||||
<td>8</td>
|
||||
<td>1</td></tr>
|
||||
<tr><td>version-number</td>
|
||||
<td>char[3]</td>
|
||||
<td>Version of Blender the file was created in; '254' means version 2.54</td>
|
||||
<td>9</td>
|
||||
<td>3</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<p>
|
||||
<a href="http://en.wikipedia.org/wiki/Endianness">Endianness</a> addresses the way values are ordered in a sequence of bytes(see the <a href="#example-endianess">example</a> below):
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>in a big endian ordering, the largest part of the value is placed on the first byte and
|
||||
the lowest part of the value is placed on the last byte,</li>
|
||||
<li>in a little endian ordering, largest part of the value is placed on the last byte
|
||||
and the smallest part of the value is placed on the first byte.</li>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
Nowadays, little-endian is the most commonly used.
|
||||
</p>
|
||||
|
||||
<a name="example-endianess"></a>
|
||||
<div class="box">
|
||||
<p onclick="location.href='#example-endianess'" class="box-title">
|
||||
Endianess Example
|
||||
</p>
|
||||
<p>
|
||||
Writing the integer <code class="evidence">0x4A3B2C1Dh</code>, will be ordered:
|
||||
<ul>
|
||||
<li>in big endian as <code class="evidence">0x4Ah</code>, <code class="evidence">0x3Bh</code>, <code class="evidence">0x2Ch</code>, <code class="evidence">0x1Dh</code></li>
|
||||
<li>in little endian as <code class="evidence">0x1Dh</code>, <code class="evidence">0x2Ch</code>, <code class="evidence">0x3Bh</code>, <code class="evidence">0x4Ah</code></li>
|
||||
</ul>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
Blender supports little-endian and big-endian.<br>
|
||||
This means that when the endianness
|
||||
is different between the blend-file and the PC your using, Blender changes it to the byte ordering
|
||||
of your PC.
|
||||
</p>
|
||||
|
||||
<a name="example-file-header"></a>
|
||||
<div class="box">
|
||||
<p onclick="location.href='#example-file-header'" class="box-title">
|
||||
File-header Example
|
||||
</p>
|
||||
|
||||
<p>
|
||||
This hex-dump describes a file-header created with <code>blender</code> <code>2.54.0</code> on <code>little-endian</code> hardware with a <code>32 bits</code> pointer length.
|
||||
<code class="block"> <span class="descr">pointer-size version-number
|
||||
| |</span>
|
||||
0000 0000: [42 4C 45 4E 44 45 52] [5F] [76] [32 35 34] BLENDER_v254 <span class="descr">
|
||||
| |
|
||||
identifier endianness</span></code>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<a name="file-blocks" href="#file-blocks"><h3>File-blocks</h3></a>
|
||||
|
||||
<p>
|
||||
File-blocks contain a "<a href="#file-block-header">file-block header</a>" and "file-block data".
|
||||
</p>
|
||||
|
||||
<a name="file-block-header" href="#file-block-header"><h3>File-block headers</h3></a>
|
||||
|
||||
<p>
|
||||
The file-block-header describes:
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>the type of information stored in the
|
||||
file-block</li>
|
||||
<li>the total length of the data</li>
|
||||
<li>the old memory
|
||||
pointer at the moment the data was written to disk</li>
|
||||
<li>the number of items of this information</li>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
As we can see below, depending on the pointer-size stored in the file-header, a file-block-header
|
||||
can be 20 or 24 bytes long, hence it is always aligned at 4 bytes.
|
||||
</p>
|
||||
|
||||
<table>
|
||||
<caption>File-block-header</caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>reference</th>
|
||||
<th>structure</th>
|
||||
<th>type</th>
|
||||
<th>offset</th>
|
||||
<th>size</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>code</td>
|
||||
<td>char[4]</td>
|
||||
<td>File-block identifier</td>
|
||||
<td>0</td>
|
||||
<td>4</td></tr>
|
||||
<tr><td>size</td>
|
||||
<td>integer</td>
|
||||
<td>Total length of the data after the file-block-header</td>
|
||||
<td>4</td>
|
||||
<td>4</td></tr>
|
||||
<tr><td>old memory address</td>
|
||||
<td>void*</td>
|
||||
<td>Memory address the structure was located when written to disk</td>
|
||||
<td>8</td>
|
||||
<td>pointer-size (4/8)</td></tr>
|
||||
<tr><td>SDNA index</td>
|
||||
<td>integer</td>
|
||||
<td>Index of the SDNA structure</td>
|
||||
<td>8+pointer-size</td>
|
||||
<td>4</td></tr>
|
||||
<tr><td>count</td>
|
||||
<td>integer</td>
|
||||
<td>Number of structure located in this file-block</td>
|
||||
<td>12+pointer-size</td>
|
||||
<td>4</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<p>
|
||||
The above table describes how a file-block-header is structured:
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li><code>Code</code> describes different types of file-blocks. The code determines with what logic the data must be read. <br>
|
||||
These codes also allows fast finding of data like Library, Scenes, Object or Materials as they all have a specific code. </li>
|
||||
<li><code>Size</code> contains the total length of data after the file-block-header.
|
||||
After the data a new file-block starts. The last file-block in the file
|
||||
has code 'ENDB'.</li>
|
||||
<li><code>Old memory address</code> contains the memory address when the structure
|
||||
was last stored. When loading the file the structures can be placed on
|
||||
different memory addresses. Blender updates pointers to these structures
|
||||
to the new memory addresses.</li>
|
||||
<li><code>SDNA index</code> contains the index in the DNA structures to be used when
|
||||
reading this file-block-data. <br>
|
||||
More information about this subject will be explained in the <a href="#reading-scene-information">Reading scene information section</a>.</li>
|
||||
<li><code>Count</code> tells how many elements of the specific SDNA structure can be found in the data.</li>
|
||||
</ul>
|
||||
|
||||
<a name="example-file-block-header"></a>
|
||||
<div class="box">
|
||||
<p onclick="location.href='#example-file-block-header'" class="box-title">
|
||||
Example
|
||||
</p>
|
||||
<p>
|
||||
This hex-dump describes a File-block (= <span class="header">File-block header</span> + <span class="data">File-block data</span>) created with <code>blender</code> <code>2.54</code> on <code>little-endian</code> hardware with a <code>32 bits</code> pointer length.<br>
|
||||
<code class="block"><span class="descr"> file-block
|
||||
identifier='SC' data size=1404 old pointer SDNA index=150
|
||||
| | | |</span>
|
||||
0000 4420: <span class="header">[53 43 00 00] [7C 05 00 00] [68 34 FB 0B] [96 00 00 00]</span> SC.. `... ./.. ....
|
||||
0000 4430: <span class="header">[01 00 00 00]</span> <span class="data">[xx xx xx xx xx xx xx xx xx xx xx xx</span> .... xxxx xxxx xxxx<span class="descr">
|
||||
| |
|
||||
count=1 file-block data (next 1404 bytes)</span>
|
||||
</code>
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>The code <code>'SC'+0x00h</code> identifies that it is a Scene. </li>
|
||||
<li>Size of the data is 1404 bytes (0x0000057Ch = 0x7Ch + 0x05h * 256 = 124 + 1280)</li>
|
||||
<li>The old pointer is 0x0BFB3468h</li>
|
||||
<li>The SDNA index is 150 (0x00000096h = 6 + 9 * 16 = 6 + 144)</li>
|
||||
<li>The section contains a single scene (count = 1).</li>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
Before we can interpret the data of this file-block we first have to read the DNA structures in the file.
|
||||
The section "<a href="#structure-DNA">Structure DNA</a>" will show how to do that.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<a name="structure-DNA" href="#structure-DNA"><h2>Structure DNA</h2></a>
|
||||
|
||||
<a name="DNA1-file-block" href="#DNA1-file-block"><h3>The DNA1 file-block</h3></a>
|
||||
|
||||
<p>
|
||||
Structure DNA is stored in a file-block with code 'DNA1'. It can be just before the 'ENDB' file-block.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The 'DNA1' file-block contains all internal structures of the Blender release the
|
||||
file was created in. <br>
|
||||
These structure can be described as C-structures: they can hold fields, arrays and
|
||||
pointers to other structures, just like a normal C-structure.
|
||||
|
||||
<p>
|
||||
<code class="block">struct SceneRenderLayer {
|
||||
struct SceneRenderLayer *next, *prev;
|
||||
char name[32];
|
||||
struct Material *mat_override;
|
||||
struct Group *light_override;
|
||||
unsigned int lay;
|
||||
unsigned int lay_zmask;
|
||||
int layflag;
|
||||
int pad;
|
||||
int passflag;
|
||||
int pass_xor;
|
||||
};
|
||||
</code>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
For example,a blend-file created with Blender 2.54 the 'DNA1' file-block is 57796 bytes long and contains 398 structures.
|
||||
</p>
|
||||
|
||||
<a name="DNA1-file-block-header" href="#DNA1-file-block-header"><h3>DNA1 file-block-header</h3></a>
|
||||
|
||||
<p>
|
||||
The DNA1 file-block header follows the same rules of any other file-block, see the example below.
|
||||
</p>
|
||||
|
||||
<a name="example-DNA1-file-block-header"></a>
|
||||
<div class="box">
|
||||
<p onclick="location.href='#example-DNA1-file-block-header'" class="box-title">
|
||||
Example
|
||||
</p>
|
||||
<p>
|
||||
This hex-dump describes the file-block 'DNA1' header created with <code>blender</code> <code>2.54.0</code> on <code>little-endian</code> hardware with a <code>32 bits</code> pointer length.<br>
|
||||
<code class="block"><span class="descr"> (file-block
|
||||
identifier='DNA1') data size=57796 old pointer SDNA index=0
|
||||
| | | |</span>
|
||||
0004 B060 <span class="header">[44 4E 41 31] [C4 E1 00 00] [C8 00 84 0B] [00 00 00 00]</span> DNA1............
|
||||
0004 B070 <span class="header">[01 00 00 00]</span> <span class="fade">[53 44 4E 41 4E 41 4D 45 CB 0B 00 00</span> ....<span class="fade">SDNANAME....</span><span class="descr">
|
||||
| |
|
||||
count=1 'DNA1' file-block data (next 57796 bytes)</span>
|
||||
</code>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<a name="DNA1-file-block-data" href="#DNA1-file-block-data"><h3>DNA1 file-block data</h3></a>
|
||||
<p>
|
||||
The next section describes how this information is ordered in the <b>data</b> of the 'DNA1' file-block.
|
||||
</p>
|
||||
|
||||
<table>
|
||||
<caption>Structure of the DNA file-block-data</caption>
|
||||
<thead>
|
||||
<tr><th colspan="2">repeat condition</th>
|
||||
<th>name</th>
|
||||
<th>type</th>
|
||||
<th>length</th>
|
||||
<th>description</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><td></td>
|
||||
<td></td>
|
||||
<td>identifier</td>
|
||||
<td>char[4]</td>
|
||||
<td>4</td>
|
||||
<td>'SDNA'</td></tr>
|
||||
<tr><td></td>
|
||||
<td></td>
|
||||
<td>name identifier</td>
|
||||
<td>char[4]</td>
|
||||
<td>4</td>
|
||||
<td>'NAME'</td></tr>
|
||||
<tr><td></td>
|
||||
<td></td>
|
||||
<td>#names</td>
|
||||
<td>integer</td>
|
||||
<td>4</td>
|
||||
<td>Number of names follows</td></tr>
|
||||
<tr><td>for(#names)</td>
|
||||
<td></td>
|
||||
<td>name</td>
|
||||
<td>char[]</td>
|
||||
<td>?</td>
|
||||
<td>Zero terminating string of name, also contains pointer and simple array definitions (e.g. '*vertex[3]\0')</td></tr>
|
||||
<tr><td></td>
|
||||
<td></td>
|
||||
<td>type identifier</td>
|
||||
<td>char[4]</td>
|
||||
<td>4</td>
|
||||
<td>'TYPE' this field is aligned at 4 bytes</td></tr>
|
||||
<tr><td></td>
|
||||
<td></td>
|
||||
<td>#types</td>
|
||||
<td>integer</td>
|
||||
<td>4</td>
|
||||
<td>Number of types follows</td></tr>
|
||||
<tr><td>for(#types)</td>
|
||||
<td></td>
|
||||
<td>type</td>
|
||||
<td>char[]</td>
|
||||
<td>?</td>
|
||||
<td>Zero terminating string of type (e.g. 'int\0')</td></tr>
|
||||
<tr><td></td>
|
||||
<td></td>
|
||||
<td>length identifier</td>
|
||||
<td>char[4]</td>
|
||||
<td>4</td>
|
||||
<td>'TLEN' this field is aligned at 4 bytes</td></tr>
|
||||
<tr><td>for(#types)</td>
|
||||
<td></td>
|
||||
<td>length</td>
|
||||
<td>short</td>
|
||||
<td>2</td>
|
||||
<td>Length in bytes of type (e.g. 4)</td></tr>
|
||||
<tr><td></td>
|
||||
<td></td>
|
||||
<td>structure identifier</td>
|
||||
<td>char[4]</td>
|
||||
<td>4</td>
|
||||
<td>'STRC' this field is aligned at 4 bytes</td></tr>
|
||||
<tr><td></td>
|
||||
<td></td>
|
||||
<td>#structures</td>
|
||||
<td>integer</td>
|
||||
<td>4</td>
|
||||
<td>Number of structures follows</td></tr>
|
||||
<tr><td>for(#structures)</td>
|
||||
<td></td>
|
||||
<td>structure type</td>
|
||||
<td>short</td>
|
||||
<td>2</td>
|
||||
<td>Index in types containing the name of the structure</td></tr>
|
||||
<tr><td>..</td>
|
||||
<td></td>
|
||||
<td>#fields</td>
|
||||
<td>short</td>
|
||||
<td>2</td>
|
||||
<td>Number of fields in this structure</td></tr>
|
||||
<tr><td>..</td>
|
||||
<td>for(#field)</td>
|
||||
<td>field type</td>
|
||||
<td>short</td>
|
||||
<td>2</td>
|
||||
<td>Index in type</td></tr>
|
||||
<tr><td>for end</td>
|
||||
<td>for end</td>
|
||||
<td>field name</td>
|
||||
<td>short</td>
|
||||
<td>2</td>
|
||||
<td>Index in name</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<p>
|
||||
As you can see, the structures are stored in 4 arrays: names, types,
|
||||
lengths and structures. Every structure also contains an array of
|
||||
fields. A field is the combination of a type and a name. From this
|
||||
information a catalog of all structures can be constructed.
|
||||
The names are stored as how a C-developer defines them. This means that
|
||||
the name also defines pointers and arrays.
|
||||
(When a name starts with '*' it is used as a pointer. when the name
|
||||
contains for example '[3]' it is used as a array of 3 long.)
|
||||
In the types you'll find simple-types (like: 'integer', 'char',
|
||||
'float'), but also complex-types like 'Scene' and 'MetaBall'.
|
||||
'TLEN' part describes the length of the types. A 'char' is 1 byte, an
|
||||
'integer' is 4 bytes and a 'Scene' is 1376 bytes long.
|
||||
</p>
|
||||
|
||||
<div class="box">
|
||||
<p class="box-title">
|
||||
Note
|
||||
</p>
|
||||
<p>
|
||||
All identifiers, are arrays of 4 chars, hence they are all aligned at 4 bytes.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<a name="example-DNA1-file-block-data"></a>
|
||||
<div class="box">
|
||||
<p onclick="location.href='#example-DNA1-file-block-data'" class="box-title">
|
||||
Example
|
||||
</p>
|
||||
<p>
|
||||
Created with <code>blender</code> <code>2.54.0</code> on <code>little-endian</code> hardware with a <code>32 bits</code> pointer length.
|
||||
</p>
|
||||
|
||||
<a name="DNA1-data-array-names" href="#DNA1-data-array-names"><h4>The names array</h4></a>
|
||||
<p>
|
||||
The first names are: *next, *prev, *data, *first, *last, x, y, xmin, xmax, ymin, ymax, *pointer, group, val, val2, type, subtype, flag, name[32], ...
|
||||
<code class="block"><span class="descr"> file-block-data identifier='SDNA' array-id='NAME' number of names=3019
|
||||
| | |</span>
|
||||
0004 B070 <span class="fade">01 00 00 00 [53 44 4E 41]</span><span class="data">[4E 41 4D 45] [CB 0B 00 00]</span> <span class="fade">....SDNA</span>NAME....
|
||||
0004 B080 <span class="data">[2A 6E 65 78 74 00][2A 70 72 65 76 00] [2A 64 61 74</span> *next.*prev.*dat<span class="descr">
|
||||
| | |
|
||||
'*next\0' '*prev\0' '*dat'</span><span class="fade">
|
||||
....
|
||||
.... (3019 names)</span>
|
||||
</code>
|
||||
</p>
|
||||
|
||||
<div class="box">
|
||||
<p class="box-title">
|
||||
Note
|
||||
</p>
|
||||
<p>
|
||||
While reading the DNA you'll will come across some strange
|
||||
names like '(*doit)()'. These are method pointers and Blender updates
|
||||
them to the correct methods.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<a name="DNA1-data-array-types" href="#DNA1-data-array-types"><h4>The types array</h4></a>
|
||||
<p>
|
||||
The first types are: char, uchar, short, ushort, int, long, ulong, float, double, void, Link, LinkData, ListBase, vec2s, vec2f, ...
|
||||
<code class="block"><span class="descr"> array-id='TYPE'
|
||||
|</span>
|
||||
0005 2440 <span class="fade">6F 6C 64 5B 34 5D 5B 34 5D 00 00 00</span> [54 59 50 45] <span class="fade">old[4][4]...</span>TYPE
|
||||
0005 2450 [C9 01 00 00] [63 68 61 72 00] [75 63 68 61 72 00][73 ....char.uchar.s<span class="descr">
|
||||
| | | |
|
||||
number of types=457 'char\0' 'uchar\0' 's'</span><span class="fade">
|
||||
....
|
||||
.... (457 types)</span>
|
||||
</code>
|
||||
</p>
|
||||
|
||||
<a name="DNA1-data-array-lengths" href="#DNA1-data-array-lengths"><h4>The lengths array</h4></a>
|
||||
<p>
|
||||
<code class="block"><span class="descr"> char uchar ushort short
|
||||
array-id length length length length
|
||||
'TLEN' 1 1 2 2</span>
|
||||
0005 3AA0 <span class="fade">45 00 00 00</span> [54 4C 45 4E] [01 00] [01 00] [02 00] [02 00] <span class="fade">E...</span>TLEN........
|
||||
<span class="fade">....</span>
|
||||
0005 3AC0 [08 00] [04 00] [08 00] [10 00] [10 00] [14 00] [4C 00] [34 00] ............L.4.<span class="descr">
|
||||
8 4 8
|
||||
ListBase vec2s vec2f ... etc
|
||||
length len length </span><span class="fade">
|
||||
....
|
||||
.... (457 lengths, same as number of types)</span>
|
||||
</code>
|
||||
</p>
|
||||
|
||||
<a name="DNA1-data-array-structures" href="#DNA1-data-array-structures"><h4>The structures array</h4></a>
|
||||
<p>
|
||||
<code class="block"><span class="descr"> array-id='STRC'
|
||||
|</span>
|
||||
0005 3E30 <span class="fade">40 00 38 00 60 00 00 00 00 00 00 00</span> [53 54 52 43] <span class="fade">@.8.`.......</span>STRC
|
||||
0005 3E40 [8E 01 00 00] [0A 00] [02 00] [0A 00] [00 00] [0A 00] [01 00] ................<span class="descr">
|
||||
398 10 2 10 0 10 0
|
||||
number of index fields index index index index
|
||||
structures in <a href="#DNA1-data-array-types">types</a> in <a href="#DNA1-data-array-types">types</a> in <a href="#DNA1-data-array-names">names</a> in <a href="#DNA1-data-array-types">types</a> in <a href="#DNA1-data-array-names">names</a></span><span class="fade">
|
||||
' '----------------' '-----------------' '
|
||||
' field 0 field 1 '
|
||||
'--------------------------------------------------------'
|
||||
structure 0
|
||||
....
|
||||
.... (398 structures, each one describeing own type, and type/name for each field)</span>
|
||||
</code>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
The DNA structures inside a Blender 2.48 blend-file can be found at <a href="http://www.atmind.nl/blender/blender-sdna.html">http://www.atmind.nl/blender/blender-sdna.html</a>.
|
||||
|
||||
If we understand the DNA part of the file it is now possible to read
|
||||
information from other parts file-blocks. The next section will tell us
|
||||
how.
|
||||
</p>
|
||||
|
||||
<a name="reading-scene-information" href="#reading-scene-information"><h2>Reading scene information</h2></a>
|
||||
|
||||
<p>
|
||||
Let us look at <a href="#example-file-block-header">the file-block header we have seen earlier</a>:<br>
|
||||
</p>
|
||||
<ul>
|
||||
<li>the file-block identifier is <code>'SC'+0x00h</code></li>
|
||||
<li>the SDNA index is 150</li>
|
||||
<li>the file-block size is 1404 bytes</li>
|
||||
</ul>
|
||||
<p>
|
||||
Now note that:
|
||||
<ul>
|
||||
<li>the structure at index 150 in the DNA is a structure of type 'Scene' (counting from 0).</li>
|
||||
<li>the associated type ('Scene') in the DNA has the length of 1404 bytes.</li>
|
||||
</ul>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
We can map the Scene structure on the data of the file-blocks.
|
||||
But before we can do that, we have to flatten the Scene-structure.
|
||||
|
||||
<code class="block">struct Scene {
|
||||
ID id; <span class="descr">// 52 bytes long (ID is different a structure)</span>
|
||||
AnimData *adt; <span class="descr">// 4 bytes long (pointer to an AnimData structure)</span>
|
||||
Object *camera; <span class="descr">// 4 bytes long (pointer to an Object structure)</span>
|
||||
World *world; <span class="descr">// 4 bytes long (pointer to an Object structure)</span>
|
||||
...
|
||||
float cursor[3]; <span class="descr">// 12 bytes long (array of 3 floats)</span>
|
||||
...
|
||||
};
|
||||
</code>
|
||||
|
||||
The first field in the Scene-structure is of type 'ID' with the name 'id'.
|
||||
Inside the list of DNA structures there is a structure defined for type 'ID' (structure index 17).
|
||||
|
||||
<code class="block">struct ID {
|
||||
void *next, *prev;
|
||||
struct ID *newid;
|
||||
struct Library *lib;
|
||||
char name[24];
|
||||
short us;
|
||||
short flag;
|
||||
int icon_id;
|
||||
IDProperty *properties;
|
||||
};
|
||||
</code>
|
||||
|
||||
The first field in this structure has type 'void' and name '*next'. <br>
|
||||
Looking in the structure list there is no structure defined for type 'void': it is a simple type and therefore the data should be read.
|
||||
The name '*next' describes a pointer.
|
||||
As we see, the first 4 bytes of the data can be mapped to 'id.next'.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Using this method we'll map a structure to its data. If we want to
|
||||
read a specific field we know at which offset in the data it is located
|
||||
and how much space it takes.<br>
|
||||
The next table shows the output of this flattening process for some
|
||||
parts of the Scene-structure. Not all rows are described in the table as
|
||||
there is a lot of information in a Scene-structure.
|
||||
</p>
|
||||
|
||||
<table>
|
||||
<caption>Flattened SDNA structure 150: Scene</caption>
|
||||
<thead>
|
||||
<tr><th>reference</th>
|
||||
<th>structure</th>
|
||||
<th>type</th><th>name</th>
|
||||
<th>offset</th><th>size</th>
|
||||
<th>description</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><td>id.next</td><td><a href="#struct:ID">ID</a></td>
|
||||
<td>void</td><td>*next</td>
|
||||
<td>0</td>
|
||||
<td>4</td>
|
||||
<td>Refers to the next scene</td></tr>
|
||||
<tr><td>id.prev</td><td><a href="#struct:ID">ID</a></td>
|
||||
<td>void</td><td>*prev</td>
|
||||
<td>4</td>
|
||||
<td>4</td>
|
||||
<td>Refers to the previous scene</td></tr>
|
||||
<tr><td>id.newid</td><td><a href="#struct:ID">ID</a></td>
|
||||
<td>ID</td><td>*newid</td>
|
||||
<td>8</td>
|
||||
<td>4</td>
|
||||
<td></td></tr>
|
||||
<tr><td>id.lib</td><td><a href="#struct:ID">ID</a></td>
|
||||
<td>Library</td><td>*lib</td>
|
||||
<td>12</td>
|
||||
<td>4</td>
|
||||
<td></td></tr>
|
||||
<tr><td>id.name</td><td><a href="#struct:ID">ID</a></td>
|
||||
<td>char</td><td>name[24]</td>
|
||||
<td>16</td>
|
||||
<td>24</td>
|
||||
<td>'SC'+the name of the scene as displayed in Blender</td></tr>
|
||||
<tr><td>id.us</td><td><a href="#struct:ID">ID</a></td>
|
||||
<td>short</td><td>us</td>
|
||||
<td>40</td>
|
||||
<td>2</td>
|
||||
<td></td></tr>
|
||||
<tr><td>id.flag</td><td><a href="#struct:ID">ID</a></td>
|
||||
<td>short</td><td>flag</td><td>42</td><td>2</td>
|
||||
<td></td></tr>
|
||||
<tr><td>id.icon_id</td><td><a href="#struct:ID">ID</a></td>
|
||||
<td>int</td><td>icon_id</td><td>44</td>
|
||||
<td>4</td>
|
||||
<td></td></tr>
|
||||
<tr><td>id.properties</td><td><a href="#struct:ID">ID</a></td>
|
||||
<td>IDProperty</td><td>*properties</td>
|
||||
<td>48</td>
|
||||
<td>4</td>
|
||||
<td></td></tr>
|
||||
<tr><td>adt</td><td>Scene</td><td>AnimData</td>
|
||||
<td>*adt</td>
|
||||
<td>52</td>
|
||||
<td>4</td>
|
||||
<td></td></tr>
|
||||
<tr><td>camera</td><td>Scene</td>
|
||||
<td>Object</td>
|
||||
<td>*camera</td>
|
||||
<td>56</td>
|
||||
<td>4</td>
|
||||
<td>Pointer to the current camera</td></tr>
|
||||
<tr><td>world</td><td>Scene</td>
|
||||
<td>World</td>
|
||||
<td>*world</td>
|
||||
<td>60</td>
|
||||
<td>4</td>
|
||||
<td>Pointer to the current world</td></tr>
|
||||
|
||||
<tr><td class="skip" colspan="7">Skipped rows</td></tr>
|
||||
|
||||
<tr><td>r.xsch</td><td><a href="#struct:RenderData">RenderData</a>
|
||||
</td><td>short</td><td>xsch</td><td>382</td><td>2</td>
|
||||
<td>X-resolution of the output when rendered at 100%</td></tr>
|
||||
<tr><td>r.ysch</td><td><a href="#struct:RenderData">RenderData</a>
|
||||
</td><td>short</td><td>ysch</td><td>384</td><td>2</td>
|
||||
<td>Y-resolution of the output when rendered at 100%</td></tr>
|
||||
<tr><td>r.xparts</td><td><a href="#struct:RenderData">RenderData</a>
|
||||
</td><td>short</td><td>xparts</td><td>386</td><td>2</td>
|
||||
<td>Number of x-part used by the renderer</td></tr>
|
||||
<tr><td>r.yparts</td><td><a href="#struct:RenderData">RenderData</a>
|
||||
</td><td>short</td><td>yparts</td><td>388</td><td>2</td>
|
||||
<td>Number of x-part used by the renderer</td></tr>
|
||||
|
||||
<tr><td class="skip" colspan="7">Skipped rows</td></tr>
|
||||
|
||||
<tr><td>gpd</td><td>Scene</td><td>bGPdata</td><td>*gpd</td><td>1376</td><td>4</td>
|
||||
<td></td></tr>
|
||||
<tr><td>physics_settings.gravity</td><td><a href="#struct:PhysicsSettings">PhysicsSettings</a>
|
||||
</td><td>float</td><td>gravity[3]</td><td>1380</td><td>12</td>
|
||||
<td></td></tr>
|
||||
<tr><td>physics_settings.flag</td><td><a href="#struct:PhysicsSettings">PhysicsSettings</a>
|
||||
</td><td>int</td><td>flag</td><td>1392</td><td>4</td>
|
||||
<td></td></tr>
|
||||
<tr><td>physics_settings.quick_cache_step</td><td><a href="#struct:PhysicsSettings">PhysicsSettings</a>
|
||||
</td><td>int</td><td>quick_cache_step</td><td>1396</td><td>4</td>
|
||||
<td></td></tr>
|
||||
<tr><td>physics_settings.rt</td><td><a href="#struct:PhysicsSettings">PhysicsSettings</a>
|
||||
</td><td>int</td><td>rt</td><td>1400</td><td>4</td>
|
||||
<td></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<p>
|
||||
We can now read the X and Y resolution of the Scene:
|
||||
<ul>
|
||||
<li>the X-resolution is located on offset 382 of the file-block-data and must be read as a
|
||||
short.</li>
|
||||
<li>the Y-resolution is located on offset 384 and is also a short</li>
|
||||
</ul>
|
||||
</p>
|
||||
|
||||
<div class="box">
|
||||
<p class="box-title">
|
||||
Note
|
||||
</p>
|
||||
<p>
|
||||
An array of chars can mean 2 things. The field contains readable
|
||||
text or it contains an array of flags (not humanly readable).
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="box">
|
||||
<p class="box-title">
|
||||
Note
|
||||
</p>
|
||||
<p>
|
||||
A file-block containing a list refers to the DNA structure and has a count larger
|
||||
than 1. For example Vertexes and Faces are stored in this way.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
Reference in New Issue
Block a user