Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
53c425a
testing with new type of animation rendering
hvfrancesco Mar 25, 2014
d3e35ea
thinking about switching to xml parsing
hvfrancesco Mar 25, 2014
9d22acd
switched to xml parsing with etree, animation is now rendered with an…
hvfrancesco Mar 26, 2014
60abe6e
updated README file
hvfrancesco Mar 26, 2014
edb318a
eliminated the module used for writing header, now every other module…
hvfrancesco Mar 26, 2014
c4ef090
updated README after the suppression of svg_header file
hvfrancesco Mar 26, 2014
1cae3b6
added xml encoding header with etree
hvfrancesco Mar 26, 2014
e9fa2a4
added quick example with blend file and svg output
hvfrancesco Mar 26, 2014
0f696a2
added a workaround to prettify the SVG output with etree
hvfrancesco Mar 27, 2014
9caf5c1
added a note in README about the needed blender version
hvfrancesco Mar 27, 2014
73cba4d
added a new module called visivle_advanced that can render the visibl…
hvfrancesco Mar 27, 2014
8a02dff
typo
hvfrancesco Mar 27, 2014
ff9a186
working on animated SVG, needs to be fixed
hvfrancesco Mar 27, 2014
04e3533
first working code with animated svg output, needs a bit more work
hvfrancesco Mar 27, 2014
f2f4a06
cleaned a bit
hvfrancesco Mar 27, 2014
813a170
moved SVG animation stuff to its own module
hvfrancesco Mar 28, 2014
9375fb2
cleaning stuff, and updating README
hvfrancesco Mar 28, 2014
1086a2f
Update README.md
hvfrancesco Mar 28, 2014
62dc486
added MIT License to README
hvfrancesco Mar 28, 2014
adf527a
added miku2 animation example
hvfrancesco Mar 28, 2014
d25d938
Update README.md
hvfrancesco Mar 28, 2014
c08c084
Update README.md
hvfrancesco Mar 28, 2014
7247fb3
modified miku
hvfrancesco Mar 28, 2014
bcb3ef3
Update README.md
hvfrancesco Mar 28, 2014
5358206
simplified miku version
hvfrancesco Mar 28, 2014
0cbf3f6
Update README.md
hvfrancesco Mar 28, 2014
cf5df9c
faster miku animation
hvfrancesco Mar 28, 2014
855efd4
Merge branch 'master' of github.com:hvfrancesco/freestylesvg
hvfrancesco Mar 28, 2014
53909c9
modified example animation
hvfrancesco Mar 28, 2014
9a56b45
Update README.md
hvfrancesco Mar 28, 2014
883066d
rounded keytimes in animate.py to 3 digits in order to produce slight…
hvfrancesco Apr 1, 2014
f4c02d9
switched to Blender 2.72 api
hvfrancesco Dec 5, 2014
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 45 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,57 @@

Python style modules for writing SVG output in Blender. Features filled contour paths.

![Table and chair](https://rawgithub.com/jlep/freestylesvg/master/example/table_and_chair.svg)
![Table and chair](https://rawgithub.com/hvfrancesco/freestylesvg/master/example/table_and_chair.svg)

*Model by [yellowpanda](http://www.blendswap.com/blends/view/69490)*

## Getting Started

Open the scripts as text blocks in your .blend file and make sure Freestyle python
scripting mode is enabled. Now you can add the scripts as style modules. `svg_header.py`
should be the first style and `svg_footer.py` should be the last.
scripting mode is enabled. Now you can add the scripts as style modules.
You can render still images or animations, the svg file will be written on the same directory of the
source blender file.

When opened in Inkscape each rendered frame will be in a separate layer, with sublayers for the different
types or rendered elements (e.g. fills, invisible lines, visible edges) according to the modules you used
to render.

IMPORTANT: this code uses the new freestyle API introduced in Blender 2.70, it won't work on earlier blender versions

## Animation in SVG

You can automatically output a SVG file that contains frame-by-frame animation using SVG native ```<animate>``` tags,
ready to be used on the web, and it will work on most modern browsers. Ain't that cool?
To do this, just add to your freestyle rendering modules the ```animate.py``` script, the same way you would do with any other module.
The 'animate' module can go in any position on your module queue, but I suggest to put it in first or last position for a cleaner SVG

![Animated SVG from Blender](https://rawgithub.com/hvfrancesco/freestylesvg/master/example/animated.svg)


![Animated SVG from Blender](https://rawgithub.com/hvfrancesco/freestylesvg/master/example/miku5.svg)

*'Miku' model by Kio*

## License

MIT
The MIT License (MIT)

Copyright (c) 2014, Francesco Fantoni - Jarno Leppänen

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
121 changes: 121 additions & 0 deletions animate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# License : MIT
# Author : Francesco Fantoni
# Date : 2014-03-28

import os
import re
import bpy

from parameter_editor import *

try:
import xml.etree.cElementTree as et
except ImportError:
import xml.etree.ElementTree as et

# set here your frames-per-second rate
fps = 10

_HEADER = """\
<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n
"""
_ROOT = '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="%d" height="%d"></svg>\n'


SVG_NS = "http://www.w3.org/2000/svg"
et.register_namespace("", SVG_NS)
et.register_namespace("sodipodi", "http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd")
et.register_namespace("inkscape", "http://www.inkscape.org/namespaces/inkscape")

scene = getCurrentScene()
current_frame = scene.frame_current
w = scene.render.resolution_x * scene.render.resolution_percentage / 100
h = scene.render.resolution_y * scene.render.resolution_percentage / 100
path = re.sub(r'\.blend$|$', '.svg' , bpy.data.filepath)

# write header if it does not yet exists
try:
with open(path) as f:
pass
except IOError:
f = open(path, "w")
f.write(_HEADER)
f.write(_ROOT % (w,h))
f.close()


# shade and write svg

tree = et.parse(path)
root = tree.getroot()


# layer for the frame
if tree.find(".//{http://www.w3.org/2000/svg}g[@id='frame_%06d']" % current_frame) is None:
layer_frame = et.XML('<g id="frame_%06d"></g>' % current_frame)
layer_frame.set('{http://www.inkscape.org/namespaces/inkscape}groupmode', 'layer')
layer_frame.set('{http://www.inkscape.org/namespaces/inkscape}label', 'frame_%06d' % current_frame)
root.append(layer_frame)
else:
layer_frame = tree.find(".//{http://www.w3.org/2000/svg}g[@id='frame_%06d']" % current_frame)

# SVG animation stuff
frames = tree.findall("*")
n_of_frames = len(frames)
keyTimes = ""
for n in range(0, n_of_frames):
keyTimes += "%.3f" % (n/n_of_frames) + ";"
keyTimes += "1"
dur = (n_of_frames / fps)

animation_tags = tree.findall(".//{http://www.w3.org/2000/svg}animate")

for n in range(0, len(animation_tags)):
values = ""
for m in range(0, len(animation_tags)):
if m == n:
values+="inline;"
else:
values+="none;"
animation_tags[n].set('values', values+"none;none")

values = ""
for n in range(1, n_of_frames):
values += "none;"
values += "inline;none"

if tree.find(".//{http://www.w3.org/2000/svg}animate[@id='anim_%06d']" % current_frame) is None:
frame_anim = et.XML('<animate id="anim_%06d" />' % current_frame)
frame_anim.set('attributeName', 'display')
frame_anim.set('values', values)
frame_anim.set('repeatCount', 'indefinite')
frame_anim.set('begin', '0s')
frame_anim.set('keyTimes', keyTimes)
frame_anim.set('dur', str(dur)+"s")
layer_frame.append(frame_anim)

for e in tree.findall(".//{http://www.w3.org/2000/svg}animate"):
e.set('keyTimes', keyTimes)
e.set('dur', str(dur)+"s")


# prettifies XML
def indent(elem, level=0):
i = "\n" + level*" "
if len(elem):
if not elem.text or not elem.text.strip():
elem.text = i + " "
if not elem.tail or not elem.tail.strip():
elem.tail = i
for elem in elem:
indent(elem, level+1)
if not elem.tail or not elem.tail.strip():
elem.tail = i
else:
if level and (not elem.tail or not elem.tail.strip()):
elem.tail = i

indent(root)

# write SVG to file
tree.write(path, encoding='UTF-8', xml_declaration=True)
Loading