Accueil > Linux / Logiciels Libres > Blender > Openshot : How to create animated 3D title with Blender

Openshot : How to create animated 3D title with Blender

jeudi 1er novembre 2012, par Yann

 Réaliser des titres animés pour OpenShot à l’aide de Blender

Retrouvez les explications complètes dans Linux Pratique 77

http://www.ed-diamond.com/feuille_lpra77/index.html

 Zip file of the animated title

Softtext.zip

 Animation Idea and automation

The first thing to do consists in creating an title animation with Blender. You can grab ideas all over the net. Just make a beautiful title in 3D wich is animated :-).

After that, you need to automate it using the API of blender. You can read lots of tutorial, watch amazing videos too. This is the big part of the work : create and automate.

It remains the integration in Openshot. That’s I’m going to explain here, based on an example.

 Soft text animation and automation

Blender capture of the working blend file

The chosen animation on this example, is made of a falling text on a rough surface. The text is made of a soft material. So when reaching the ground, the text bounces and trembles before stopping. In the simulation, we can add a cylinder to show more physical effects.

Source code of the automation (using Blender API)

In this source code, we manage the possibility to hide the red cylinder and its physical effect on our soft text. The control variable is name “cylinder”

import bpy
from bpy.props import *
from math import pi
 
cylinder ='1'
 
#http://www.sebastianbauer.name/archive/117
def moveToLayer( object, layer ):
    layers = [False]*20
    layers[layer] = True
    object.layers = layers
 
def createSoftText(title,extrude,bevel_depth,spacemode,textsize,width,font):
 
    if cylinder=='1':
        bpy.data.objects['Cylinder'].hide_render = False
        bpy.data.objects['Cylinder'].hide = False
        bpy.ops.object.select_all(action='DESELECT')
        #selecting Ground
        bpy.context.scene.objects.active  = bpy.data.objects['Cylinder']
        bpy.context.scene.objects.active.select = True
        #if bpy.data.objects['Ground'].modifiers.keys()[0] != 'Collision':
        if bpy.data.objects['Cylinder'].modifiers.find('Collision') == -1:
            #print("collision  = empty")
            bpy.ops.object.modifier_add(type="COLLISION")
        else:
            print("OK")
    else:
        bpy.ops.object.select_all(action='DESELECT')
        #selecting Cylinder
        bpy.context.scene.objects.active  = bpy.data.objects['Cylinder']
        bpy.context.scene.objects.active.select = True
        if bpy.data.objects['Cylinder'].modifiers.find('Collision') != -1:
            bpy.ops.object.modifier_remove(modifier="Collision")
        bpy.data.objects['Cylinder'].hide = True
        bpy.data.objects['Cylinder'].hide_render = True
 
    bpy.ops.object.select_all(action='DESELECT')
 
    bpy.ops.object.text_add(view_align=False, enter_editmode=False,location=(0, 0, 0), rotation=(0, 0, 0))
    ActiveObjectText = bpy.context.scene.objects.active
    newtext = bpy.context.scene.objects.active
    #erasing previous objects
    if bpy.context.scene.objects.active.name != 'Text':
        bpy.ops.object.select_all(action='DESELECT')
        #selecting and erasing Text
        bpy.context.scene.objects.active  = bpy.data.objects['Text']
        bpy.context.scene.objects.active.select = True
        bpy.ops.object.delete(use_global=False)
        bpy.ops.object.select_all(action='DESELECT')
        #need to delete other objects
        bpy.ops.object.select_all(action='DESELECT')
        #selecting and erasing Turbulence field
        bpy.context.scene.objects.active  = bpy.data.objects['Enveloppe2']
        bpy.context.scene.objects.active.select = True
        bpy.ops.object.delete(use_global=False)
        #selecting newText
        bpy.context.scene.objects.active  = newtext
        bpy.context.scene.objects.active.select = True
    #naming/renaming the text
    bpy.context.scene.objects.active.name = 'Text';
    bpy.context.scene.objects.active = bpy.data.objects['Text']
    #rotating text
    ActiveObjectText.rotation_euler[0]=0.0 #xaxis
    ActiveObjectText.rotation_euler[1]=0.0  #yaxis
    ActiveObjectText.rotation_euler[2]=0.0  #zaxis
    ActiveObjectText.location[0]=0
    ActiveObjectText.location[1]=-9.75
    ActiveObjectText.location[2]=5 #dur to shadow on the ground
    #changing text
    ActiveObjectText.data.body = title
    #centering text
    ActiveObjectText.data.align= spacemode
    #extrude text
    ActiveObjectText.data.extrude=extrude
    #bevel text
    ActiveObjectText.data.bevel_depth = bevel_depth
    ActiveObjectText.data.bevel_resolution = 5
    #text size
    ActiveObjectText.data.size = textsize
    bpy.context.scene.objects.active.data.space_character = width
    bpy.context.scene.objects.active.data.font = font
    #convert to mesh to apply effect
    bpy.ops.object.convert(target='MESH', keep_original=False)
    #put origin to center of text
    #bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY', center='MEDIAN')
    # Don't use Median, because it does not always put the origin to the center!
    bpy.ops.object.origin_set(type='GEOMETRY_ORIGIN', center='BOUNDS')
    #affect material
    ActiveObjectText.data.materials.append(bpy.data.materials['MAT_PRE_Obsidian'])
    #copy the pre loopecut cube
    bpy.ops.object.select_all(action='DESELECT')
    #obsolete
    #bpy.ops.object.select_name(name='Enveloppe')
    bpy.context.scene.objects.active = bpy.context.scene.objects['Enveloppe']
    #activate in 3D
    bpy.context.scene.objects.active.select = True
    bpy.ops.object.duplicate(linked=False, mode='TRANSLATION')
    #bpy.ops.object.duplicate_move(OBJECT_OT_duplicate={"linked":False, "mode":'TRANSLATION'}, TRANSFORM_OT_translate={"value":(0, 0, 0), "constraint_axis":(False, False, False), "constraint_orientation":'GLOBAL', "mirror":False, "proportional":'DISABLED', "proportional_edit_falloff":'SMOOTH', "proportional_size":1, "snap":False, "snap_target":'CLOSEST', "snap_point":(0, 0, 0), "snap_align":False, "snap_normal":(0, 0, 0), "texture_space":False, "release_confirm":False})
    ActiveObjectEnveloppe = bpy.context.scene.objects.active
    ActiveObjectEnveloppe.name = 'Enveloppe2'
    ActiveObjectEnveloppe.draw_type = 'WIRE'
    ActiveObjectEnveloppe.hide_render = True
    #need to be on the visible layer to do duplication?
    #moving object
    ActiveObjectEnveloppe.location[0]=0
    ActiveObjectEnveloppe.location[1]=-9.75
    ActiveObjectEnveloppe.location[2]=5
    # mo   ve to layer 0 (first one)
    #moveToLayer(ActiveObjectEnveloppe,0)
    #adjust size of existed envelope to text
    IncrementX = 0.3
    IncrementY = 0.3
    IncrementZ = 0.15
    ActiveObjectEnveloppe.dimensions = (ActiveObjectText.dimensions[0]+IncrementX),(ActiveObjectText.dimensions[1]+IncrementY), (ActiveObjectText.dimensions[2]+IncrementZ)
    #bpy.ops.object.origin_set(type='GEOMETRY_ORIGIN', center='MEDIAN')
    bpy.ops.object.origin_set(type='GEOMETRY_ORIGIN', center='BOUNDS')
 
    bpy.ops.object.modifier_add(type='SOFT_BODY')
    #link text to enveloppe
    bpy.ops.object.select_all(action='DESELECT')
    #obsolete
    #bpy.ops.object.select_name(name='Enveloppe')
    bpy.context.scene.objects.active = ActiveObjectText
    #activate in 3D
    bpy.context.scene.objects.active.select = True
    bpy.ops.object.modifier_add(type='MESH_DEFORM')
    #configuring mesh deform
    ActiveObjectText.modifiers['MeshDeform'].object = ActiveObjectEnveloppe
    ActiveObjectText.modifiers['MeshDeform'].precision = 6 #<6 does not work!!! grid precision?
    #binding text to cube
    bpy.ops.object.meshdeform_bind(modifier="MeshDeform")
    #softbody configuration
    ActiveObjectEnveloppe.soft_body.use_goal=False
    ActiveObjectEnveloppe.soft_body.use_stiff_quads = True
    ActiveObjectEnveloppe.soft_body.plastic=10
    ActiveObjectEnveloppe.data.update()
 
    bpy.ops.ptcache.free_bake_all()   # erase baked dynamics
    bpy.ops.ptcache.bake_all() # bake dynamics : take time but needed before rendering animation
 
 
font = bpy.data.fonts["Bfont"] #don't forget to save datablock with Bfont :-)
extrude = 0.1
bevel_depth = 0.01
spacemode = 'CENTER'
text_size = 1.0
width = 1.0
title="OpenShot 1.4.3"
 
createSoftText(title,extrude,bevel_depth,spacemode,text_size,width,font)

Result image (number 85 in animation)

Making of

 Integration

How it works

Now the animation is full fonctionning in Blender (You can try it with the blend file embedded in the ZIP archive). We have to use it with Openshot. Before we need to understand how an animation made with Blender can be used in Openshot.

For that, Openshot use the following synoptic :

Openshot uses 4 files to be able to propose to the user a new 3D title :

  • An icon file which represents the final render of the title in th e Openshot GUI
  • An XML file which contains the GUI interface for configuring the 3D title
  • A blender file, the main file, which will be use by Blender to generate the animation
  • An python file which contains the core code for modifying the blender file according to configuration made by the user in the GUI. All the modification will be given to the blend file when rendering.
    These files are located in different directories under /usr/share/pyshared/openshot/blender :
$ tree -d /usr/share/pyshared/openshot/blender
/usr/share/pyshared/openshot/blender
├── blend
├── earth
├── icons
└── scripts
 
4 directories

The « blender » directory contains the « XML » files of the titles GUI configurators. The « blend » directory contains originals Blender files which embed animation on a generic text. The « earth » directory contains NASA images for the realistic earth title. The “icons” directory contains icons for the title GUI. The “scripts” directory contains python files which link Openshot to Blender for rendering. Theses files are the most difficult to write. They contains all the automation of the animation, and the Blender API commands which modify the original blender file to suit the user’s configuration.

So we need to create these files for our soft text animation.

When the 3D Title is configured, you can launched rendering. At this time, Openshot is calling Blender in commandline mode like that :

Blender command: /home/yann/Téléchargements/blender-2.62-linux-glibc27-x86_64/blender -b '/usr/lib/pymodules/python2.7/openshot/blender/blend/explode.blend' -P '/home/yann/.openshot/blender/cdd6b89e-1439-11e2-a257-0024d6b0a314/explode.py'
update_image: /home/yann/.openshot/blender/cdd6b89e-1439-11e2-a257-0024d6b0a314/TitleFileName0075.png
Blender render thread finished

Blender is run with two parameters :

  • « -b » indicates the blend file to use and render in background. This option allows to use Openshot during the render ;
  • « -P » indicates the python script to be executed with the blend file.

The sample image is written in a subdirectory of your home « /.openshot/blender ».

Creation from previous animation

Except for the icon file, we will use existing files from the Openshot distribution software to create the new ones.

Here is our new icon :

Other file are created by copying existing files :

$ cp /home/yann/Documents/articles_lpmag/article_titre_openshot/images/title_icon.png ./icons/softtext.png
$ cp fly_by_1.xml softtext.xml
$ cp /home/yann/blender/softtext/mysofttext_openshot.blend blend/softtext.blend
$ cp scripts/fly_by_1.py scripts/softtext.py

Modification of the XML file for interface

We modify the head of the file to suit the new animation :

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE openshot-effect>
<effect>
    <title translatable="True">Falling Soft Text</title>
    <description translatable="True">The soft boby title is on the ground</description>
    <icon>softtext.png</icon>
    <category>Video</category>
    <service>softtext.blend</service>

And in order to manage the cylinder in the animation, we add a dropdown menu in th e GUI :

    <param name="cylinder_on_off" type="dropdown" title="Display Cylinder" description="">
        <values>
            <value name="Yes" num="1"/>
            <value name="No" num="0"/>
        </values>
    <default>1</default>

Finally modify the animation duration to fit you blend file animation length :

    <param name="end_frame" type="spinner" title="End Frame" description="">
        <min>144</min>
        <max>144</max>
        <default>144</default>
    </param>

Even if the new title is not yet operational, you can see the new GUI for our sofftext by launching Openshot :

Modification of the python script

We need to modify the python script to match the new 3D title.

This file is globally divided in 3 parts :

  • Inclusions and funtions ;
  • Initialisation of all the parameters ;
  • Modification of the Blender file parameters from the Openshot GUI.

Just after the “loadfont” function we put our “createSoftText” function

def createSoftText(title,extrude,bevel_depth,spacemode,textsize,width,font):
 
    if cylinder=='1':
        bpy.data.objects['Cylinder'].hide_render = False
        bpy.data.objects['Cylinder'].hide = False
        bpy.ops.object.select_all(action='DESELECT')
        #selecting Ground
        bpy.context.scene.objects.active  = bpy.data.objects['Cylinder']
        bpy.context.scene.objects.active.select = True
        #if bpy.data.objects['Ground'].modifiers.keys()[0] != 'Collision':
        if bpy.data.objects['Cylinder'].modifiers.find('Collision') == -1:
            #print("collision  = empty")
            bpy.ops.object.modifier_add(type="COLLISION")
        #else:
            #print("OK")
    else:
        bpy.ops.object.select_all(action='DESELECT')
        #selecting Cylinder
        bpy.context.scene.objects.active  = bpy.data.objects['Cylinder']
        bpy.context.scene.objects.active.select = True
        if bpy.data.objects['Cylinder'].modifiers.find('Collision') != -1:
            bpy.ops.object.modifier_remove(modifier="Collision")
        bpy.data.objects['Cylinder'].hide = True
        bpy.data.objects['Cylinder'].hide_render = True
 
    bpy.ops.object.select_all(action='DESELECT')
 
    bpy.ops.object.text_add(view_align=False, enter_editmode=False,location=(0, 0, 0), rotation=(0, 0, 0))
    ActiveObjectText = bpy.context.scene.objects.active
    newtext = bpy.context.scene.objects.active
    #erasing previous objects
    if bpy.context.scene.objects.active.name != 'Text':
        bpy.ops.object.select_all(action='DESELECT')
        #selecting and erasing Text
        bpy.context.scene.objects.active  = bpy.data.objects['Text']
        bpy.context.scene.objects.active.select = True
        bpy.ops.object.delete(use_global=False)
        bpy.ops.object.select_all(action='DESELECT')
        #need to delete other objects
        bpy.ops.object.select_all(action='DESELECT')
        #selecting and erasing Turbulence field
        bpy.context.scene.objects.active  = bpy.data.objects['Enveloppe2']
        bpy.context.scene.objects.active.select = True
        bpy.ops.object.delete(use_global=False)
        #selecting newText
        bpy.context.scene.objects.active  = newtext
        bpy.context.scene.objects.active.select = True
    #naming/renaming the text
    bpy.context.scene.objects.active.name = 'Text';
    bpy.context.scene.objects.active = bpy.data.objects['Text']
    #rotating text
    ActiveObjectText.rotation_euler[0]=0.0 #xaxis
    ActiveObjectText.rotation_euler[1]=0.0  #yaxis
    ActiveObjectText.rotation_euler[2]=0.0  #zaxis
    ActiveObjectText.location[0]=0
    ActiveObjectText.location[1]=-9.75
    ActiveObjectText.location[2]=5 #dur to shadow on the ground
    #changing text
    ActiveObjectText.data.body = title
    #centering text
    ActiveObjectText.data.align= spacemode
    #extrude text
    ActiveObjectText.data.extrude=extrude
    #bevel text
    ActiveObjectText.data.bevel_depth = bevel_depth
    ActiveObjectText.data.bevel_resolution = 5
    #text size
    ActiveObjectText.data.size = textsize
    bpy.context.scene.objects.active.data.space_character = width
    bpy.context.scene.objects.active.data.font = font
    #convert to mesh to apply effect
    bpy.ops.object.convert(target='MESH', keep_original=False)
    #put origin to center of text
    #bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY', center='MEDIAN')
    # Don't use Median, because it does not always put the origin to the center!
    bpy.ops.object.origin_set(type='GEOMETRY_ORIGIN', center='BOUNDS')
    #affect material
    ActiveObjectText.data.materials.append(bpy.data.materials['MAT_PRE_Obsidian'])
    #copy the pre loopecut cube
    bpy.ops.object.select_all(action='DESELECT')
    #obsolete
    #bpy.ops.object.select_name(name='Enveloppe')
    bpy.context.scene.objects.active = bpy.context.scene.objects['Enveloppe']
    #activate in 3D
    bpy.context.scene.objects.active.select = True
    bpy.ops.object.duplicate(linked=False, mode='TRANSLATION')
    #bpy.ops.object.duplicate_move(OBJECT_OT_duplicate={"linked":False, "mode":'TRANSLATION'}, TRANSFORM_OT_translate={"value":(0, 0, 0), "constraint_axis":(False, False, False), "constraint_orientation":'GLOBAL', "mirror":False, "proportional":'DISABLED', "proportional_edit_falloff":'SMOOTH', "proportional_size":1, "snap":False, "snap_target":'CLOSEST', "snap_point":(0, 0, 0), "snap_align":False, "snap_normal":(0, 0, 0), "texture_space":False, "release_confirm":False})
    ActiveObjectEnveloppe = bpy.context.scene.objects.active
    ActiveObjectEnveloppe.name = 'Enveloppe2'
    ActiveObjectEnveloppe.draw_type = 'WIRE'
    ActiveObjectEnveloppe.hide_render = True
    #need to be on the visible layer to do duplication?
    #moving object
    ActiveObjectEnveloppe.location[0]=0
    ActiveObjectEnveloppe.location[1]=-9.75
    ActiveObjectEnveloppe.location[2]=5
    # mo   ve to layer 0 (first one)
    #moveToLayer(ActiveObjectEnveloppe,0)
    #adjust size of existed envelope to text
    IncrementX = 0.3
    IncrementY = 0.3
    IncrementZ = 0.15
    ActiveObjectEnveloppe.dimensions = (ActiveObjectText.dimensions[0]+IncrementX),(ActiveObjectText.dimensions[1]+IncrementY), (ActiveObjectText.dimensions[2]+IncrementZ)
    #bpy.ops.object.origin_set(type='GEOMETRY_ORIGIN', center='MEDIAN')
    bpy.ops.object.origin_set(type='GEOMETRY_ORIGIN', center='BOUNDS')
 
    bpy.ops.object.modifier_add(type='SOFT_BODY')
    #link text to enveloppe
    bpy.ops.object.select_all(action='DESELECT')
    #obsolete
    #bpy.ops.object.select_name(name='Enveloppe')
    bpy.context.scene.objects.active = ActiveObjectText
    #activate in 3D
    bpy.context.scene.objects.active.select = True
    bpy.ops.object.modifier_add(type='MESH_DEFORM')
    #configuring mesh deform
    ActiveObjectText.modifiers['MeshDeform'].object = ActiveObjectEnveloppe
    ActiveObjectText.modifiers['MeshDeform'].precision = 6 #<6 does not work!!! grid precision?
    #binding text to cube
    bpy.ops.object.meshdeform_bind(modifier="MeshDeform")
    #softbody configuration
    ActiveObjectEnveloppe.soft_body.use_goal=False
    ActiveObjectEnveloppe.soft_body.use_stiff_quads = True
    ActiveObjectEnveloppe.soft_body.plastic=10
    ActiveObjectEnveloppe.data.update()
 
    bpy.ops.ptcache.free_bake_all()   # erase baked dynamics
    bpy.ops.ptcache.bake_all() # bake dynamics : take time but needed before rendering animation

Important : The two last lines bakes all the physics stuff which can be long. But if you don’t do that, your sample image will be false, because the history of the animation is not taken into account while Openshot makes render only for one frame for sample images.

After that, we need to add the new cylinder variable in the params area :

# Init all of the variables needed by this script.  Because Blender executes
# this script, OpenShot will inject a dictionary of the required parameters
# before this script is executed.
params = {
            'title' : 'Oh Yeah! OpenShot!',
            'extrude' : 0.05,
            'bevel_depth' : 0.01,
            'spacemode' : 'CENTER',
            'text_size' : 1,
            'width' : 1.0,
            'fontname' : 'Bfont',
 
            'color' : [0.8,0.8,0.8],
            'alpha' : 1.0,
 
            'output_path' : '/tmp/',
            'fps' : 24,
            'quality' : 90,
            'file_format' : 'PNG',
            'color_mode' : 'RGBA',
            'horizon_color' : [0, 0, 0],
            'resolution_x' : 1920,
            'resolution_y' : 1080,
            'resolution_percentage' : 100,
            'start_frame' : 20,
            'end_frame' : 25,
            'animation' : True,
        }

The next step, is made of comments. We need to comment some old python stuff on unused text object :

#text_object = bpy.data.curves["txtName1"]
#text_object.extrude = params["extrude"]
#text_object.bevel_depth = params["bevel_depth"]
#text_object.body = params["title"]
#text_object.align = params["spacemode"]
#text_object.size = params["text_size"]
#text_object.space_character = params["width"]

Same thing for the font which is managed by the function now :

# set the font
#text_object.font = font

Next we call the main function with the GUI parameters :

# function call createSoftText(title,extrude,bevel_depth,spacemode,textsize,width,font,cylinder)
createSoftText(params["title"],params["extrude"],params["bevel_depth"],params["spacemode"],params["text_size"],params["width"], font, params["cylinder_on_off"])

Finally, we ajust the material pointer to the our text material in the blend file :

# Change the material settings (color, alpha, etc...)
#material_object = bpy.data.materials["Material.001"]
material_object = bpy.data.materials['MAT_PRE_Obsidian'] #match the text material in Blend file

Here the full code of the softtext.py file

#    OpenShot Video Editor is a program that creates, modifies, and edits video files.
#   Copyright (C) 2009  Jonathan Thomas
#
#    This file is part of OpenShot Video Editor (http://launchpad.net/openshot/).
#
#    OpenShot Video Editor 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 3 of the License, or
#    (at your option) any later version.
#
#    OpenShot Video Editor 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 OpenShot Video Editor.  If not, see <http://www.gnu.org/licenses/>.
 
 
# Import Blender's python API.  This only works when the script is being
# run from the context of Blender.  Blender contains it's own version of Python
# with this library pre-installed.
import bpy
 
# Load a font
def load_font(font_path):
    """ Load a new TTF font into Blender, and return the font object """
    # get the original list of fonts (before we add a new one)
    original_fonts = bpy.data.fonts.keys()
 
    # load new font
    bpy.ops.font.open(filepath=font_path)
 
    # get the new list of fonts (after we added a new one)
    for font_name in bpy.data.fonts.keys():
        if font_name not in original_fonts:
            return bpy.data.fonts[font_name]
 
    # no new font was added
    return None
 
def createSoftText(title,extrude,bevel_depth,spacemode,textsize,width,font,cylinder):
    """ Create an animated falling softbody text """
    if cylinder=='1':
        bpy.data.objects['Cylinder'].hide_render = False
        bpy.data.objects['Cylinder'].hide = False
        bpy.ops.object.select_all(action='DESELECT')
        #selecting Ground
        bpy.context.scene.objects.active  = bpy.data.objects['Cylinder']
        bpy.context.scene.objects.active.select = True
        #if bpy.data.objects['Ground'].modifiers.keys()[0] != 'Collision':
        if bpy.data.objects['Cylinder'].modifiers.find('Collision') == -1:
            #print("collision  = empty")
            bpy.ops.object.modifier_add(type="COLLISION")
        #else:
            #print("OK")
    else:
        bpy.ops.object.select_all(action='DESELECT')
        #selecting Cylinder
        bpy.context.scene.objects.active  = bpy.data.objects['Cylinder']
        bpy.context.scene.objects.active.select = True
        if bpy.data.objects['Cylinder'].modifiers.find('Collision') != -1:
            bpy.ops.object.modifier_remove(modifier="Collision")
        bpy.data.objects['Cylinder'].hide = True
        bpy.data.objects['Cylinder'].hide_render = True
 
    bpy.ops.object.select_all(action='DESELECT')
 
    bpy.ops.object.text_add(view_align=False, enter_editmode=False,location=(0, 0, 0), rotation=(0, 0, 0))
    ActiveObjectText = bpy.context.scene.objects.active
    newtext = bpy.context.scene.objects.active
    #erasing previous objects
    if bpy.context.scene.objects.active.name != 'Text':
        bpy.ops.object.select_all(action='DESELECT')
        #selecting and erasing Text
        bpy.context.scene.objects.active  = bpy.data.objects['Text']
        bpy.context.scene.objects.active.select = True
        bpy.ops.object.delete(use_global=False)
        bpy.ops.object.select_all(action='DESELECT')
        #need to delete other objects
        bpy.ops.object.select_all(action='DESELECT')
        #selecting and erasing Turbulence field
        bpy.context.scene.objects.active  = bpy.data.objects['Enveloppe2']
        bpy.context.scene.objects.active.select = True
        bpy.ops.object.delete(use_global=False)
        #selecting newText
        bpy.context.scene.objects.active  = newtext
        bpy.context.scene.objects.active.select = True
    #naming/renaming the text
    bpy.context.scene.objects.active.name = 'Text';
    bpy.context.scene.objects.active = bpy.data.objects['Text']
    #rotating text
    ActiveObjectText.rotation_euler[0]=0.0 #xaxis
    ActiveObjectText.rotation_euler[1]=0.0  #yaxis
    ActiveObjectText.rotation_euler[2]=0.0  #zaxis
    ActiveObjectText.location[0]=0
    ActiveObjectText.location[1]=-9.75
    ActiveObjectText.location[2]=5 #dur to shadow on the ground
    #changing text
    ActiveObjectText.data.body = title
    #centering text
    ActiveObjectText.data.align= spacemode
    #extrude text
    ActiveObjectText.data.extrude=extrude
    #bevel text
    ActiveObjectText.data.bevel_depth = bevel_depth
    ActiveObjectText.data.bevel_resolution = 5
    #text size
    ActiveObjectText.data.size = textsize
    bpy.context.scene.objects.active.data.space_character = width
    bpy.context.scene.objects.active.data.font = font
    #convert to mesh to apply effect
    bpy.ops.object.convert(target='MESH', keep_original=False)
    #put origin to center of text
    #bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY', center='MEDIAN')
    # Don't use Median, because it does not always put the origin to the center!
    bpy.ops.object.origin_set(type='GEOMETRY_ORIGIN', center='BOUNDS')
    #affect material
    ActiveObjectText.data.materials.append(bpy.data.materials['MAT_PRE_Obsidian'])
    #copy the pre loopecut cube
    bpy.ops.object.select_all(action='DESELECT')
    #obsolete
    #bpy.ops.object.select_name(name='Enveloppe')
    bpy.context.scene.objects.active = bpy.context.scene.objects['Enveloppe']
    #activate in 3D
    bpy.context.scene.objects.active.select = True
    bpy.ops.object.duplicate(linked=False, mode='TRANSLATION')
    #bpy.ops.object.duplicate_move(OBJECT_OT_duplicate={"linked":False, "mode":'TRANSLATION'}, TRANSFORM_OT_translate={"value":(0, 0, 0), "constraint_axis":(False, False, False), "constraint_orientation":'GLOBAL', "mirror":False, "proportional":'DISABLED', "proportional_edit_falloff":'SMOOTH', "proportional_size":1, "snap":False, "snap_target":'CLOSEST', "snap_point":(0, 0, 0), "snap_align":False, "snap_normal":(0, 0, 0), "texture_space":False, "release_confirm":False})
    ActiveObjectEnveloppe = bpy.context.scene.objects.active
    ActiveObjectEnveloppe.name = 'Enveloppe2'
    ActiveObjectEnveloppe.draw_type = 'WIRE'
    ActiveObjectEnveloppe.hide_render = True
    #need to be on the visible layer to do duplication?
    #moving object
    ActiveObjectEnveloppe.location[0]=0
    ActiveObjectEnveloppe.location[1]=-9.75
    ActiveObjectEnveloppe.location[2]=5
    # mo   ve to layer 0 (first one)
    #moveToLayer(ActiveObjectEnveloppe,0)
    #adjust size of existed envelope to text
    IncrementX = 0.3
    IncrementY = 0.3
    IncrementZ = 0.15
    ActiveObjectEnveloppe.dimensions = (ActiveObjectText.dimensions[0]+IncrementX),(ActiveObjectText.dimensions[1]+IncrementY), (ActiveObjectText.dimensions[2]+IncrementZ)
    #bpy.ops.object.origin_set(type='GEOMETRY_ORIGIN', center='MEDIAN')
    bpy.ops.object.origin_set(type='GEOMETRY_ORIGIN', center='BOUNDS')
 
    bpy.ops.object.modifier_add(type='SOFT_BODY')
    #link text to enveloppe
    bpy.ops.object.select_all(action='DESELECT')
    #obsolete
    #bpy.ops.object.select_name(name='Enveloppe')
    bpy.context.scene.objects.active = ActiveObjectText
    #activate in 3D
    bpy.context.scene.objects.active.select = True
    bpy.ops.object.modifier_add(type='MESH_DEFORM')
    #configuring mesh deform
    ActiveObjectText.modifiers['MeshDeform'].object = ActiveObjectEnveloppe
    ActiveObjectText.modifiers['MeshDeform'].precision = 6 #<6 does not work!!! grid precision?
    #binding text to cube
    bpy.ops.object.meshdeform_bind(modifier="MeshDeform")
    #softbody configuration
    ActiveObjectEnveloppe.soft_body.use_goal=False
    ActiveObjectEnveloppe.soft_body.use_stiff_quads = True
    ActiveObjectEnveloppe.soft_body.plastic=10
    ActiveObjectEnveloppe.data.update()
 
    bpy.ops.ptcache.free_bake_all()   # erase baked dynamics
    bpy.ops.ptcache.bake_all() # bake dynamics : take time but needed before rendering animation
 
 
 
# Debug Info:
# ./blender -b test.blend -P demo.py
# -b = background mode
# -P = run a Python script within the context of the project file
 
# Init all of the variables needed by this script.  Because Blender executes
# this script, OpenShot will inject a dictionary of the required parameters
# before this script is executed.
params = {
            'title' : 'Oh Yeah! OpenShot!',
            'extrude' : 0.1,
            'bevel_depth' : 0.02,
            'spacemode' : 'CENTER',
            'text_size' : 1.5,
            'width' : 1.0,
            'fontname' : 'Bfont',
            'cylinder_on_off' : '1',
 
            'color' : [0.8,0.8,0.8],
            'alpha' : 1.0,
 
            'output_path' : '/tmp/',
            'fps' : 24,
            'quality' : 90,
            'file_format' : 'PNG',
            'color_mode' : 'RGBA',
            'horizon_color' : [0.57, 0.57, 0.57],
            'resolution_x' : 1920,
            'resolution_y' : 1080,
            'resolution_percentage' : 100,
            'start_frame' : 20,
            'end_frame' : 25,
            'animation' : True,
        }
 
#INJECT_PARAMS_HERE
 
# The remainder of this script will modify the current Blender .blend project
# file, and adjust the settings.  The .blend file is specified in the XML file
# that defines this template in OpenShot.
#----------------------------------------------------------------------------
 
# Modify Text / Curve settings
#print (bpy.data.curves.keys())
#text_object = bpy.data.curves["txtName1"]
#text_object.extrude = params["extrude"]
#text_object.bevel_depth = params["bevel_depth"]
#text_object.body = params["title"]
#text_object.align = params["spacemode"]
#text_object.size = params["text_size"]
#text_object.space_character = params["width"]
 
# Get font object
font = None
if params["fontname"] != "Bfont":
    # Add font so it's available to Blender
    font = load_font(params["fontname"])
else:
    # Get default font
    font = bpy.data.fonts["Bfont"]
 
# set the font
#text_object.font = font
# function call createSoftText(title,extrude,bevel_depth,spacemode,textsize,width,font,cylinder)
createSoftText(params["title"],params["extrude"],params["bevel_depth"],params["spacemode"],params["text_size"],params["width"], font, params["cylinder_on_off"])
 
# Change the material settings (color, alpha, etc...)
#material_object = bpy.data.materials["Material.001"]
material_object = bpy.data.materials['MAT_PRE_Obsidian'] #match the text material in Blend file
material_object.diffuse_color = params["diffuse_color"]
material_object.specular_color = params["specular_color"]
material_object.specular_intensity = params["specular_intensity"]
material_object.alpha = params["alpha"]
 
# Set the render options.  It is important that these are set
# to the same values as the current OpenShot project.  These
# params are automatically set by OpenShot
bpy.context.scene.render.filepath = params["output_path"]
bpy.context.scene.render.fps = params["fps"]
try:
    bpy.context.scene.render.file_format = params["file_format"]
    bpy.context.scene.render.color_mode = params["color_mode"]
except:
    bpy.context.scene.render.image_settings.file_format = params["file_format"]
    bpy.context.scene.render.image_settings.color_mode = params["color_mode"]
bpy.data.worlds[0].horizon_color = params["horizon_color"]
bpy.context.scene.render.resolution_x = params["resolution_x"]
bpy.context.scene.render.resolution_y = params["resolution_y"]
bpy.context.scene.render.resolution_percentage = params["resolution_percentage"]
bpy.context.scene.frame_start = params["start_frame"]
bpy.context.scene.frame_end = params["end_frame"]
 
# Animation Speed (use Blender's time remapping to slow or speed up animation)
animation_speed = int(params["animation_speed"])    # time remapping multiplier
new_length = int(params["end_frame"]) * animation_speed    # new length (in frames)
bpy.context.scene.frame_end = new_length
bpy.context.scene.render.frame_map_old = 1
bpy.context.scene.render.frame_map_new = animation_speed
if params["start_frame"] == params["end_frame"]:
    bpy.context.scene.frame_start = params["end_frame"]
    bpy.context.scene.frame_end = params["end_frame"]
 
# Render the current animation to the params["output_path"] folder
bpy.ops.render.render(animation=params["animation"])

And now the result in Openshot :