Coder Social home page Coder Social logo

modular_tree's Introduction

Mtree

Mtree (previously Modular Tree) is a library for making 3d trees. It comes as an addon for blender but the c++ library can be used separately.

Table of contents

Installation (blender addon)

Go to the latest release. Under Assets, select the version corresponding to your os.
Follow the blender documentation to install the downloaded addon.

Development

Dependencies

  • Cmake
  • Blender 2.93 or higher (if you want to to develop the blender addon)

Installation

  1. Clone the repository reccursively git clone --recursive https://github.com/MaximeHerpin/modular_tree
  2. Execute the build_mtree bash script corresponding to your platform.
  3. If all went well, a cmake project has been generated under mtree/build.
  4. You can bundle the blender addon by calling the addon bundling script.

Usage

A Tree is generated by executing a succession of TreeFunction. When being executed, a TreeFunction modifies the structure of the tree, and then calls children functions recursively.
For example, a basic tree has a trunk and branches on the trunk. Such a tree can be generated as such:

auto trunk = std::make_shared<TrunkFunction>();
auto branches = std::make_shared<BranchFunction>();
trunk->add_child(branches); // branches are added on top of the trunk
Tree tree(trunk);
tree.execute_functions(); // The tree structure is generated
ManifoldMesher mesher; // A mesher is responsible of converting a tree into a 3d mesh. The ManifoldMesher ensures a smooth topology
mesher.radial_resolution = 32;
Mesh tree_mesh = mesher.mesh_tree(tree); // the resulting mesh contains the geometry of the tree in the form of vertices and triangles

A second layer of branches can be grown on top of the branches by adding another branch function as a child of the first branch function:

auto branches_primary = std::make_shared<BranchFunction>();
auto branches_secondary = std::make_shared<BranchFunction>();
branches_primary.add_child(branches_secondary); // the secondray branches will be distributed on top of the primary branches

Some trees have healthy branches as well as a layer of thin dead branches along the trunk. This can be achieved by adding to branch functions with different parameters on the trunk:

auto trunk = std::make_shared<TrunkFunction>();
auto branches_healthy = std::make_shared<BranchFunction>();
auto branches_dead = std::make_shared<BranchFunction>();
branches_dead.length = RandomProperty{.1f,1f}; // dead branches will have a length between 10cm and 1m.
branches_dead.start_radius = ConstantProperty{.05f}; // dead branches will have a radius equal to 5% of the parent nodes

trunk->add_child(branches_healthy); // both sets of branches are grown on top of the trunk
trunk->add_child(branches_dead);
Tree tree(trunk);

License

Blender being under the GPL license, the blender addon (all files under python_classes as well as __init__.py) is under the GPLv3 license.
The Mtree library is under the MIT license.

modular_tree's People

Contributors

ekaj2 avatar kilbee avatar maximeherpin avatar prevumax avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

modular_tree's Issues

Tree creation using the blender node editor

The tree creation panel is a bit messy as there are a lot of properties. The process of creating a tree is very step based (configure the roots then the trunk the n the branches...) so a more visual workflow can be less overwhelming.

The node editor is perfect to do so.
The user can choose to use the toolbar panel or just select a node tree to work with.

The nodes will be:
• Roots , the first node.
• Trunk
• branches
• Modifiers (gravity, forces, pruning...)
• Tree output
• Inputs (radius based, iteration based or constants using a curve node)

This will allow much more simplicity while giving the opportunity to control all the parameters.

Grid size not respected when using grease pencil

The grid size property for batch tree generation has no effect for using grease pencil. The trees get generated at the origin of the GP stroke. This should either be disabled in the UI, or it should do a post transformation of the trees.

tutorials - leaves

I try some features but I'm block at leaves.
Where I can find some video tutorials?

Make twig bug

Make twig is causing some somewhat strange issues. See comments below...

Grease Pencil Trunk Bug

Index out of range error if the step size is set too high:

Traceback (most recent call last):
File "/Users/ek/Library/Application Support/Blender/2.78/scripts/addons/modular_tree/generator_operators.py", line 50, in execute
create_tree(scene.cursor_location)
File "/Users/ek/Library/Application Support/Blender/2.78/scripts/addons/modular_tree/tree_creator.py", line 1107, in create_tree
grease_dir = grease_points[curr_grease_point+1] - grease_points[curr_grease_point]
IndexError: list index out of range

location: :-1

Handlers not removed when the addon is unload

If the addon is unload, it doesn't remove the 'update_all_trees()' handler from bpy.app.handlers.scene_update_post().

Because 'mtree_props' is removed from the scene, the 'update_all_trees()' fills the console with error messages.

"Make twig" button

It would be great to have a button to make a simple twig with leaves. I think twigs work much better than simple leaves when used as particles.

Progress Display

Show progress in the terminal window.

I know how to do this (and I have already built modules to do so). I just need a 'percent' progress report throughout the generation code, and I should be able to hook in the rest.

As a side note, if you could start dropping some inline comments as you work (especially in the generation code), that would be great, as I feel lost in that area right now.

Move UI to Separate Panels for Organization

I feel like this would feel cleaner and easier to use if the settings were moved to separate panels so that they could be easily collapsed. For a panel, along with the other blender settings, you can set bl_options = {'DEFAULT_CLOSED'} for not as important ones and users can open them to see the "advanced" stuff.

I feel like this would be good for you to do because I am not too familiar with this yet, and you can really rethink organization. However, if you feel the current ui boxes organize things pretty well, I can setup the panels with the boxes as the categories, and you can refine them later. Just assign someone to this or close it so I know what's up.

Moving Add-On to a Zip Format : |

I would like to discuss this a bit to see if you are interested. Please let me know what you think.

Pros:

  • Cleaner, easier to debug/manage
  • Easy to add built-in presets
  • Easy to add built-in twig models (like how The Grove does twigs)
  • Better for users
  • I have made a zip add-on reloader to help with update time

Cons:

  • Slower to update add-on in Blender for testing (Blender must be restarted on every update)

New uv unwrapping system

There is a way to unwrap manually with bmesh without using the unwrap operator. It will allow the uvs to be much cleaner, and will probably reduce the computation time.

Presets

Could really use some presets...

Should be able to save all settings to a file for future reference and get them back based on an enum. I've something similar to this before so I think I'll give it an initial shot at least.

Adding Docstrings

Really need some docstrings along with code comments so anyone can find their way around the code.

Built-in Presets

Some presets would also help to understand the different parameters and be like a starting tutorial to learn from.

Smooth vertex group weight for leaves

Currently the leave's vertex group isn't very smooth. Perhaps this was intentional, but if not, I just found this while researching smoothing weights:

http://blender.stackexchange.com/a/58920/28015

The first method would work rather nicely in this case :-)

(of course it should be set to "Active Group" so as not to modify other groups, but it does have some other really useful parameters that could be exposed in modular tree's ui)

Update can't delete with visualize particles

Update can't delete with visualize particles enabled...the poll method will fail b/c the context is incorrect (delete doesn't work in weight paint mode).

A few possible solutions (in rough order):

  1. delete before entering weight paint
  2. don't visualize the particles with update operator
  3. remove the visualize particles feature

Quick Refactoring

Removing:

  • Commented code (not comments of course)
  • Unused variables (PyCharm will help with that)

Adding

  • White spaces where needed for PEP-8 compliance (not line length compliant)

Changing

  • Non-camel-case class names
  • Capital letters in variable/function/method names

Settings complexity mode

As the number of settings is getting a bit overwhelming, it would be great to have two modes for the UI:
• Simple mode, where only the most important settings are displayed, good for beginners.
• Complete mode, all settings

Performance

Let's try to get this thing runnin' fast! Here is a brief performance report of the create_tree function:

Using 15 branch iterations on my MacBook Pro
Processor: 2.5 GHz Intel Core i7
Memory: 16 GB 1600 MHz DDR3

create_tree took 2.5 seconds
    uv took 1.4 seconds
    armature took 1.0 seconds

I added reports for the "major" parts of the create_tree function with reasonable settings (these are not accurate reports, but they certainly give the big picture). Clearly, the uv and armature creation is what's having the biggest impact on performance.

Update Tree Error

An exception is raised when no object is selected and update tree is called. This needs to be caught, and an error report needs to go to the user with self.report({'ERRO...

Bug with low iterations

Enable unwrap, set iterations to 1 and turn off roots...voila :)

I will look at this later but if you know the cleanest way to fix it right off the bat let me know.

addon_add_Modular_tree.py, line 867, in create_tree
test[b][0] = True
IndexError: list index out of range

Armature bug

when create armature is enabled, script crashes:

tree_creator.py", line 944, in create_tree
    mtree_props.objects.active = arm
AttributeError: 'ModularTreePropertyGroup' object has no attribute 'objects'

Avoid using the Scene namespace for all your properties.

I like this add-on, and I want to share one concern.

issue

I've noticed you have a few very generic variable names registered directly into bpy.types.Scene.* This increases the probability that your properties will interfere with other add-ons registering properties in Scene. It's nice to code a bit defensively here.

solution.

You can register a property collection / property group to keep all properties under your own namespace

See the docs, (tho I feel these are a bit rubbish)

import bpy

## some panel somewhere
def draw(self, context):
    self.layout.prop(context.scene.my_prop_grp, 'custom_1')


class MyPropertyGroup(bpy.types.PropertyGroup):
    custom_1 = bpy.props.FloatProperty(name="My Float")
    custom_2 = bpy.props.IntProperty(name="My Int")

def register():
    bpy.utils.register_class(MyPropertyGroup)
    bpy.types.Scene.my_prop_grp = bpy.props.PointerProperty(type=MyPropertyGroup)

def unregister():
    bpy.utils.unregister_class(MyPropertyGroup)

    # deleting the group, removes automatically all children. :)
    del bpy.types.Scene.my_prop_grp

or like this..

class SomeAddonProperties(bpy.types.PropertyGroup):

    @classmethod
    def register(cls):
        Scn = bpy.types.Scene

        Scn.some_addon_props = PointerProperty(
            name="some addon's internal global properties",
            description="some add-on uses these properties for shared properties between operators",
            type=cls,
        )

        cls.custom_1 = bpy.props.FloatProperty(name="My Float")
        cls.custom_2 = bpy.props.IntProperty(name="My Int")


    @classmethod
    def unregister(cls):
        del bpy.types.Scene.some_addon_props


def register():
    ...
    bpy.utils.register_class(SomeAddonProperties)

def unregister():
    ...
    bpy.utils.unregister_class(SomeAddonProperties)   

Obstacle Non-Applied Position

If you add an obstacle then hit update it doesn't seem to take it into account.

Please fix on release-1.0 branch again or just close this if you don't think its important.

question about curve mapping

Hi there,

i would like to know where you found the information from the curve_mapping panel. I would like to add a curve panel for a different addon, but cant find information about it.

Sorry for asking here. Didnt found a email adres

Best regards,

Rombout

Trouble with Git

UGH! Something is really messed up :-(

  • I don't know where, but somehow the icon imports were removed from init.py
  • kilbee's pull request was going to MASTER ={ (it won't last long, so someone will have to redo it before the next release)

Draw branches with grease pencil

I have a few ideas on how to do this.

  • The user draw a few lines with the grease pencil.
  • If a line is not currently a path for a branch, and if a branch passes near a point of the line, the branch will follow the line.

This is the very basic idea, that can be complexified when needed.

Roots

A better root system is needed, here are few points to work on:

  • Checking roots options changes the tree shape, adding roots at the end surely will prevent it.

  • The roots does not look natural, the generation process has to be complexified.

  • Currently roots stay under the local xy axis, a "stay under or over object" option would be nice.

Unittests

I am adding some unit tests...they are run in the if __name__ == '__main__' section, so whenever the scripts reloader loads the file they get run. I did need to strip down the script reloader addon to get the printouts to the console gone (so that the unittest printout would show cleanly). Here is my edited version of it if you would like to use it:

"""
script_watcher.py: Reload watched script upon changes.

Copyright (C) 2015 Isaac Weaver
Author: Isaac Weaver <[email protected]>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
"""

bl_info = {
    "name": "Script Watcher",
    "author": "Isaac Weaver",
    "version": (0, 7),
    "blender": (2, 75, 0),
    "location": "Properties > Scene > Script Watcher",
    "description": "Reloads an external script on edits.",
    "warning": "Still in beta stage.",
    "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/Scripts/Development/Script_Watcher",
    "tracker_url": "https://github.com/wisaac407/blender-script-watcher/issues/new",
    "category": "Development",
}

import os
import sys
import io
import traceback
import types
import subprocess

import bpy
from bpy.app.handlers import persistent


@persistent
def load_handler(dummy):
    running = bpy.context.scene.sw_settings.running

    # First of all, make sure script watcher is off on all the scenes.
    for scene in bpy.data.scenes:
        bpy.ops.wm.sw_watch_end({'scene': scene})

    # Startup script watcher on the current scene if needed.
    if running and bpy.context.scene.sw_settings.auto_watch_on_startup:
        bpy.ops.wm.sw_watch_start()


def isnum(s):
    return s[1:].isnumeric() and s[0] in '-+1234567890'


# Define the script watching operator.
class WatchScriptOperator(bpy.types.Operator):
    """Watches the script for changes, reloads the script if any changes occur."""
    bl_idname = "wm.sw_watch_start"
    bl_label = "Watch Script"

    _timer = None
    _running = False
    _times = None
    filepath = None

    def get_paths(self):
        """Find all the python paths surrounding the given filepath."""

        dirname = os.path.dirname(self.filepath)

        paths = []
        filepaths = []

        for root, dirs, files in os.walk(dirname, topdown=True):
            if '__init__.py' in files:
                paths.append(root)
                for f in files:
                    filepaths.append(os.path.join(root, f))
            else:
                dirs[:] = []  # No __init__ so we stop walking this dir.

        # If we just have one (non __init__) file then return just that file.
        return paths, filepaths or [self.filepath]

    def get_mod_name(self):
        """Return the module name and the root path of the givin python file path."""
        dir, mod = os.path.split(self.filepath)

        # Module is a package.
        if mod == '__init__.py':
            mod = os.path.basename(dir)
            dir = os.path.dirname(dir)

        # Module is a single file.
        else:
            mod = os.path.splitext(mod)[0]

        return mod, dir

    def remove_cached_mods(self):
        """Remove all the script modules from the system cache."""
        paths, files = self.get_paths()
        for mod_name, mod in list(sys.modules.items()):
            if hasattr(mod, '__file__') and os.path.dirname(mod.__file__) in paths:
                del sys.modules[mod_name]

    def _reload_script_module(self):
        print('Reloading script:', self.filepath)
        self.remove_cached_mods()
        try:
            f = open(self.filepath)
            paths, files = self.get_paths()

            # Get the module name and the root module path.
            mod_name, mod_root = self.get_mod_name()

            # Create the module and setup the basic properties.
            mod = types.ModuleType('__main__')
            mod.__file__ = self.filepath
            mod.__path__ = paths
            mod.__package__ = mod_name

            # Add the module to the system module cache.
            sys.modules[mod_name] = mod

            # Fianally, execute the module.
            exec (compile(f.read(), self.filepath, 'exec'), mod.__dict__)
        except IOError:
            print('Could not open script file.')
        except:
            sys.stderr.write("There was an error when running the script:\n" + traceback.format_exc())
        else:
            f.close()

    def reload_script(self, context):
        """Reload this script while printing the output to blenders python console."""

        # Run the script.
        self._reload_script_module()

    def modal(self, context, event):
        if not context.scene.sw_settings.running:
            self.cancel(context)
            return {'CANCELLED'}

        if context.scene.sw_settings.reload:
            context.scene.sw_settings.reload = False
            self.reload_script(context)
            return {'PASS_THROUGH'}

        if event.type == 'TIMER':
            for path in self._times:
                cur_time = os.stat(path).st_mtime

                if cur_time != self._times[path]:
                    self._times[path] = cur_time
                    self.reload_script(context)

        return {'PASS_THROUGH'}

    def execute(self, context):
        if context.scene.sw_settings.running:
            return {'CANCELLED'}

        # Grab the settings and store them as local variables.
        self.filepath = bpy.path.abspath(context.scene.sw_settings.filepath)

        # If it's not a file, doesn't exist or permistion is denied we don't precede.
        if not os.path.isfile(self.filepath):
            self.report({'ERROR'}, 'Unable to open script.')
            return {'CANCELLED'}

        # Setup the times dict to keep track of when all the files where last edited.
        dirs, files = self.get_paths()
        self._times = dict(
            (path, os.stat(path).st_mtime) for path in files)  # Where we store the times of all the paths.
        self._times[files[0]] = 0  # We set one of the times to 0 so the script will be loaded on startup.

        # Setup the event timer.
        wm = context.window_manager
        self._timer = wm.event_timer_add(0.1, context.window)
        wm.modal_handler_add(self)

        context.scene.sw_settings.running = True
        return {'RUNNING_MODAL'}

    def cancel(self, context):
        wm = context.window_manager
        wm.event_timer_remove(self._timer)

        self.remove_cached_mods()

        context.scene.sw_settings.running = False


class CancelScriptWatcher(bpy.types.Operator):
    """Stop watching the current script."""
    bl_idname = "wm.sw_watch_end"
    bl_label = "Stop Watching"

    def execute(self, context):
        # Setting the running flag to false will cause the modal to cancel itself.
        context.scene.sw_settings.running = False
        return {'FINISHED'}


class ReloadScriptWatcher(bpy.types.Operator):
    """Reload the current script."""
    bl_idname = "wm.sw_reload"
    bl_label = "Reload Script"

    def execute(self, context):
        # Setting the reload flag to true will cause the modal to cancel itself.
        context.scene.sw_settings.reload = True
        return {'FINISHED'}


# Create the UI for the operator. NEEDS FINISHING!!
class ScriptWatcherPanel(bpy.types.Panel):
    """UI for the script watcher."""
    bl_label = "Script Watcher"
    bl_idname = "SCENE_PT_script_watcher"
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW'
    bl_context = "scene"

    def draw(self, context):
        layout = self.layout
        running = context.scene.sw_settings.running

        col = layout.column()
        col.prop(context.scene.sw_settings, 'filepath')
        col.prop(context.scene.sw_settings, 'auto_watch_on_startup')
        col.operator('wm.sw_watch_start', icon='VISIBLE_IPO_ON')
        col.enabled = not running

        if running:
            row = layout.row(align=True)
            row.operator('wm.sw_watch_end', icon='CANCEL')
            row.operator('wm.sw_reload', icon='FILE_REFRESH')


class ScriptWatcherSettings(bpy.types.PropertyGroup):
    """All the script watcher settings."""
    running = bpy.props.BoolProperty(default=False)
    reload = bpy.props.BoolProperty(default=False)

    filepath = bpy.props.StringProperty(
        name='Script',
        description='Script file to watch for changes.',
        subtype='FILE_PATH'
    )

    auto_watch_on_startup = bpy.props.BoolProperty(
        name='Watch on startup',
        description='Watch script automatically on new .blend load',
        default=False
    )


def register():
    bpy.utils.register_module(__name__)

    bpy.types.Scene.sw_settings = \
        bpy.props.PointerProperty(type=ScriptWatcherSettings)

    bpy.app.handlers.load_post.append(load_handler)


def unregister():
    bpy.utils.unregister_module(__name__)

    bpy.app.handlers.load_post.remove(load_handler)

    del bpy.types.Scene.sw_settings


if __name__ == "__main__":
    register()

Pruning

This would consist of three things:

  • Control the tree boundaries with an envelop object (very easy to do)
  • Control the branch density (make branches end when there is already too many branches per unit of volume)
  • Create a "fight for light", meaning using a sun lamp to control areas of the tree where the growth is stimulated.

For the last two I think the best way would be to store density and illumination data in a 3d list, each voxel containing the probability of all branches in it to survive.

It shouldn't be difficult, but I'm afraid it will affect the computation time quite a lot, especially the third one.

Replace armature system with displace modifier

I am working on a new method of animating the trees with a displacement modifier instead of an armature. It looks promising, and will be much better suited for "sweeping wind" animations in a forest of trees. Removing the armature system will reduce the complexity of this add-on a lot and will speed up the creation process. What do you think? Should I remove the armature features entirely on this feature branch or just add this feature on top? I think this will make it much easier to animate.

Simple Twig Improvements

The Twig generator gives really good looking branches. Would be great to add the generated twig automatically to the tree's particle system or best to let the user choose the twig in your addon's panel. It would allow to use custom hand-made twigs without going in the 1000 of options of the particle system. Some scale option near to it would also be helpfull as the default scale of 0.15 is really small for the twigs. And it would allow to keep those values after an update.

Update Tree Armature Bug

If you create a tree, then hit update, it keeps the old armature around :(

Please fix this on the release-1.0 branch and then make a pull request for merging it into develop. I will review both pull requests (merging the release into master and develop) and merge them. I would also like to do some more testing before merging the release into master. I'll let you know before merging into master, so you can pump the first "major release" on blender artists.

A super cool thing about the git branching model is that an end user can come along and download master whenever they want, and they will get the latest "stable" version.

IMPORTANT! CHANGE NAME!

The version on master won't work right now! Please change the name of the repo to "modular_tree". I will update the installation instructions in a couple hours. The name is in the settings panel on github.

Blender 2.79 error

Hello,
I have an error in the blender version 2.79 is a fix for this bug online

Traceback (most recent call last):
File "C:\Program Files\Blender Foundation\Blender\2.79\scripts\addons\modular_tree\generator_operators.py", line 49, in execute
alt_create_tree(self, scene.cursor_location)
File "C:\Program Files\Blender Foundation\Blender\2.79\scripts\addons\modular_tree\tree_creator.py", line 1929, in alt_create_tree
node_tree = bpy.data.node_groups[mtree_props.node_tree]
KeyError: 'bpy_prop_collection[key]: key "" not found'

location: :-1

Finish trunk before starting branches

It is pretty hard to control the general shape of the tree, and lower branches are generated before higher branches resulting in an often conical tree.

To have more control over this, it would be nice to have the option to finish the trunk before allowing the branches to grow.
This only applies when the preserve trunk property is checked.

This will result in more cylindrical trees, which shapes will then be easily modified with the nodes.

Sent from my Sony F5121 using FastHub

Missing property definition when trying to save preset.

Missing property when trying to save preset:

Traceback (most recent call last):
File "(...)Blender Foundation\Blender\2.78\scripts\addons\modular_tree\presets.py", line 122, in execute
int(mtree_props.finish_unwrap),
AttributeError: 'ModularTreePropertyGroup' object has no attribute 'finish_unwrap'

location: :-1

Created quick fix with placeholders for missing properties: pull request

Auto Saving Blend File

Auto-save blend, all images, and all text files before generation. (of course this will need addon user prefs as well to see if the user wants it)

This is important for if the user wants to kill blender because perhaps he set the number of iterations way to high for his computer, but he doesn't want to lose his unsaved work.

Better leaves orientation

Following the idea used for the twig leaves, but generating an emitter composed of planes where each plane is the source of a leaf/twig.

With this, the leaves will have the same direction as the branches they are generated from.

Presets

Please post your presets here, and I will check them over and include them if possible.

To get to your presets, go to the folder where blender is on your system...then go to something like:

~/Blender/2.77/scripts/addons/mod_tree_presets/MyPresetName.mtp

Open that folder and add a comment here.

Please use the format:

MyPresetName
========================

paste ENTIRE preset file here...

========================

and any notes you would like to include...

Thank you for supporting the Blender community!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.