Free alternative Scatter addon?

Are there any good free alternatives to Scatter addon? Want to make grass + flowers without doing it all myself.

Hi.
Scatter Objects addon, it is included within blender. You must enable it.

Thanks but a little too basic.

Hi @espr3ss0
there’s a free early version available

You can achieve triple clusters really easily

added a random range option in the custom slot editor, so that the same custom scattering operator could give different result because of size and offset randomisation.

i’ll try to do more range randomisation tomorrow ? what kind of parameters would you like to see ? more would be killer no ? Shall i work on other procedural texture ? right now the only possibility is the cloud texture.

custom

# "Scatter" Add-on
# Copyright (C) 2019 Dorian Borremans aka BD3D
#
# 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 3 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, see <http://www.gnu.org/licenses/>.
# <pep8 compliant>

bl_info = {
    "name" : "Scatter [BD3D]", 
    "author" : "BD3D",   
    "description" : "The scattering tool of 2.8",
    "blender" : (2, 80, 0),
    "location" : "Operator",
    "warning" : "",
    "category" : "Generic"
}

import bpy
import random
from bpy.types import Menu
import webbrowser
from bpy.types import Operator, AddonPreferences, PropertyGroup
from bpy.props import StringProperty, IntProperty, BoolProperty, FloatProperty
import rna_keymap_ui
context = bpy.context

def find_collection(context, item):
    collections = item.users_collection
    if len(collections) > 0:
        return collections[0]
    return context.scene.collection

def make_collection(collection_name, parent_collection):
    if collection_name in bpy.data.collections:
        return bpy.data.collections[collection_name]
    else:
        new_collection = bpy.data.collections.new(collection_name)
        parent_collection.children.link(new_collection)
        return new_collection

######################################################################################
######################################################################################
# # # # # # # # # # # #         SCATTER ADDON PREF             # # # # # # # # # # # # 
######################################################################################
######################################################################################

class ScatterPref(AddonPreferences):
    bl_idname = __name__

    scatter_01_count = IntProperty( 
            name="Emission Number",
            subtype='NONE',
            default=1000,
            min=0,
            )#percentage couvert? 
    scatter_01_seed = IntProperty( 
            name="Emission Seed Value",
            subtype='NONE',
            default=5,
            min=0,
            )
    scatter_01_seed_is_random = BoolProperty( 
            name="Emission Random Seed Value",
            subtype='NONE',
            default=True,
            )
    scatter_01_particle_size = FloatProperty(
            name="Render Scale",
            subtype='NONE',
            default=0.25,
            min=0.01,
            max=3
            )
    scatter_01_size_random = FloatProperty(
            name="Render Scale Randomness",
            subtype='NONE',
            default=0.35,
            min=0,
            max=1
            )
    scatter_01_phase_factor_random = FloatProperty(
            name="Rotation Randomize Phase",
            subtype='NONE',
            default=2,
            min=0,
            max=2
            )
    scatter_01_display_percentage = IntProperty(
            name="Viewport Display Percentage",
            subtype='NONE',
            default=100,
            min=0,
            max=100
            )

    scatter_01_noise_scale = FloatProperty(
            name="Texture Noise Size",
            subtype='NONE',
            default=0.45,
            min=0.01,
            max=100
            )
    scatter_01_noise_depth = IntProperty(
            name="Texture Noise Depth",
            subtype='NONE',
            default=0,
            min=0,
            max=20
            )
    scatter_01_contrast = FloatProperty(
            name="Texture Color Contrast",
            subtype='NONE',
            default=3,
            min=0,
            max=5
            )
    scatter_01_intensity = FloatProperty(
            name="Texture Color Brightness",
            subtype='NONE',
            default=1,
            min=0,
            max=2
            )
    scatter_01_density_factor = FloatProperty(
            name="Texture Influence Density",
            subtype='NONE',
            default=1,
            min=0,
            max=1
            )
    scatter_01_length_factor = FloatProperty(
            name="Texture Influence Length",
            subtype='NONE',
            default=0.3,
            min=0,
            max=1
            )

    scatter_01_scalex = FloatProperty(
            name="Texture Mapping size X",
            subtype='NONE',
            default=1,
            min=-100,
            max=100
            )
    scatter_01_scaley = FloatProperty(
            name="Texture Mapping size Y",
            subtype='NONE',
            default=1,
            min=-100,
            max=100
            )
    scatter_01_scalez = FloatProperty(
            name="Texture Mapping size Z",
            subtype='NONE',
            default=1,
            min=-100,
            max=100
            )

    scatter_01_offsetx = FloatProperty(
            name="Texture Mapping offset X in meters",
            subtype='NONE',
            default=0,
            min=-10,
            max=10
            )
    scatter_01_offsety = FloatProperty(
            name="Texture Mapping offset Y in meters",
            subtype='NONE',
            default=0,
            min=-10,
            max=10
            )
    scatter_01_offsetz = FloatProperty(
            name="Texture Mapping offset Z in meters",
            subtype='NONE',
            default=0,
            min=-10,
            max=10
            )


    scatter_01_size_is_random = BoolProperty( 
            name="Texture Mapping Random Size Value XYZ",
            subtype='NONE',
            default=False,
            )
    scatter_01_size_A = FloatProperty(
            name="Size Possibilities Range From A",
            subtype='NONE',
            default=0.7,
            min=-100,
            max=100
            )
    scatter_01_size_B = FloatProperty(
            name="To B",
            subtype='NONE',
            default=1.4,
            min=-100,
            max=100
            )


    scatter_01_offset_is_random = BoolProperty( 
            name="Texture Mapping Random Offset Value XYZ in meters",
            subtype='NONE',
            default=True,
            )
    scatter_01_offset_A = FloatProperty(
            name="Offset Possibilities Range From A",
            subtype='NONE',
            default=0,
            min=-10,
            max=10
            )
    scatter_01_offset_B = FloatProperty(
            name="To B",
            subtype='NONE',
            default=10,
            min=-10,
            max=10
            )


    scatter_01_open= bpy.props.BoolProperty(
        name="Show Debug Tools",
        description="Expand me senpai",
        default=False
    )
######################################################################################

    def draw(self, context): #DRAWING HERE - DRAWING HERE - DRAWING HERE- DRAWING HERE
        layout = self.layout
        full= "------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"
        addon_prefs = context.preferences.addons[__name__].preferences

        box = layout.box()
        col = box.column()
        row = col.row(align=True)
        row.alignment = 'LEFT'
        row.prop(self,
                 'scatter_01_open',
                 text="Custom Scattering Slot 01",
                 emboss=False,
                 icon="PLAY" if self.scatter_01_open else "PLAY")
        if self.scatter_01_open:

            col.label(text=full)            

            col.prop(self,"scatter_01_count")
            if addon_prefs.scatter_01_seed_is_random == True:
                col.prop(self,"scatter_01_seed_is_random")
            else:
                col.prop(self,"scatter_01_seed_is_random")
                col.prop(self,"scatter_01_seed")
            col.prop(self,"scatter_01_particle_size")
            col.prop(self,"scatter_01_size_random")
            col.prop(self,"scatter_01_phase_factor_random")

            col.label(text=' ')
            col.prop(self,"scatter_01_density_factor")
            col.prop(self,"scatter_01_length_factor")

            col.label(text=' ')
            col.prop(self,"scatter_01_offset_is_random")
            if addon_prefs.scatter_01_offset_is_random == True:
                col.prop(self,"scatter_01_offset_A")
                col.prop(self,"scatter_01_offset_B")
            else:
                col.prop(self,"scatter_01_offsetx")
                col.prop(self,"scatter_01_offsety")
                col.prop(self,"scatter_01_offsetz")
            
            col.label(text=' ')
            col.prop(self,"scatter_01_size_is_random")
            if addon_prefs.scatter_01_size_is_random == True:
                col.prop(self,"scatter_01_size_A")
                col.prop(self,"scatter_01_size_B")
            else:
                col.prop(self,"scatter_01_scalex")
                col.prop(self,"scatter_01_scaley")
                col.prop(self,"scatter_01_scalez")

            col.label(text=' ')
            col.prop(self,"scatter_01_noise_scale")
            col.prop(self,"scatter_01_noise_depth")
            col.prop(self,"scatter_01_contrast")
            col.prop(self,"scatter_01_intensity")

            col.label(text=' ')
            col.prop(self,"scatter_01_display_percentage")

 


######################################################################################
######################################################################################
# # # # # # # # # # # #        SCATTER OPERATOR                # # # # # # # # # # # # 
######################################################################################
######################################################################################

class Scatter_OT_Custom01(bpy.types.Operator):
    bl_idname = "scatter.custom01"
    bl_label = "custom scattering operator 01"
    bl_description = ""

    def execute(self, context):
        context = bpy.context
        addon_prefs = context.preferences.addons[__name__].preferences
        scene = context.scene
        A = context.object
        ### ### Naming
        slotname = "Custom1"
        if len(bpy.context.selected_objects) > 2:
            particlename = bpy.context.selected_objects[1].name + " ..."
        else:
            particlename = bpy.context.selected_objects[1].name #BUG why the f does the name change when multiple execution ???
        pref = "SCATTER: ["+slotname+"] ["+particlename+"] v."
        i = 1
        name = "%s%d" % (pref, i)
        while A.modifiers.get(name):    
            i += 1
            name = "%s%d" % (pref, i)
        ### ### add particle system + rename and add selection in new coll
        m = A.modifiers.new(name, type='PARTICLE_SYSTEM')
        A.select_set(state=False)
        for o in bpy.context.selected_objects:
            o_collection = find_collection(bpy.context, o)
            new_collection = make_collection(name , o_collection)
            new_collection.objects.link(o)
            o_collection.objects.unlink(o)
        A.select_set(state=True)
        ### ### particle parameters
        ps = m.particle_system
        ps.name = name
        ps.settings.name = name
        ps.settings.type = 'HAIR'
        ps.settings.render_type = 'COLLECTION'
        ps.settings.instance_collection = bpy.data.collections[name]
        ps.settings.particle_size = addon_prefs.scatter_01_particle_size
        ps.settings.hair_length = 4
        ps.settings.size_random = addon_prefs.scatter_01_size_random
        ps.settings.count = addon_prefs.scatter_01_count
        if addon_prefs.scatter_01_seed_is_random == True:
            bpy.context.object.particle_systems[name].seed = random.randint(0,10000)
        else:
            bpy.context.object.particle_systems[name].seed = addon_prefs.scatter_01_seed
        ps.settings.display_percentage = addon_prefs.scatter_01_display_percentage
        ps.settings.use_advanced_hair = True
        ps.settings.use_rotations = True
        ps.settings.rotation_factor_random = 1
        ps.settings.phase_factor = 1
        ps.settings.phase_factor_random = addon_prefs.scatter_01_phase_factor_random
        ### ### texture parameters
        bpy.ops.texture.new()
        texturename = name
        bpy.data.textures[-1].name = texturename
        bpy.data.textures[texturename].type = 'CLOUDS' #LATER SUPPORT OTHER NOISE TYPE AND OPTIONS
        bpy.data.textures[texturename].noise_scale = addon_prefs.scatter_01_noise_scale
        bpy.data.textures[texturename].noise_depth = addon_prefs.scatter_01_noise_depth
        bpy.data.textures[texturename].contrast = addon_prefs.scatter_01_contrast
        bpy.data.textures[texturename].intensity = addon_prefs.scatter_01_intensity
        ps.settings.texture_slots.add().texture = bpy.data.textures[texturename]
        ps.settings.texture_slots[0].blend_type = 'MULTIPLY'
        ps.settings.texture_slots[0].use_map_time = False
        ps.settings.texture_slots[0].use_map_density = True
        ps.settings.texture_slots[0].density_factor = addon_prefs.scatter_01_density_factor
        ps.settings.texture_slots[0].use_map_length = True 
        ps.settings.texture_slots[0].length_factor = addon_prefs.scatter_01_length_factor

        if addon_prefs.scatter_01_offset_is_random == False:
            ps.settings.texture_slots[0].scale[1] = addon_prefs.scatter_01_scalex
            ps.settings.texture_slots[0].scale[2] = addon_prefs.scatter_01_scaley
            ps.settings.texture_slots[0].scale[0] = addon_prefs.scatter_01_scalez
        else:
            randomn =round(random.uniform(addon_prefs.scatter_01_size_A,addon_prefs.scatter_01_size_B), 2)
            ps.settings.texture_slots[0].scale[1] = randomn
            ps.settings.texture_slots[0].scale[2] = randomn
            ps.settings.texture_slots[0].scale[0] = randomn

        if addon_prefs.scatter_01_offset_is_random == False:
            ps.settings.texture_slots[0].offset[1] = addon_prefs.scatter_01_offsetx
            ps.settings.texture_slots[0].offset[2] = addon_prefs.scatter_01_offsety
            ps.settings.texture_slots[0].offset[0] = addon_prefs.scatter_01_offsetz
        else:
            randomno =round(random.uniform(addon_prefs.scatter_01_offset_A,addon_prefs.scatter_01_offset_B), 2)
            ps.settings.texture_slots[0].offset[1] = randomno
            ps.settings.texture_slots[0].offset[2] = randomno
            ps.settings.texture_slots[0].offset[0] = randomno

        ps.settings.texture_slots[0].texture_coords = 'GLOBAL'
        return {'FINISHED'}

######################################################################################
######################################################################################
# # # # # # # # # # # #            N MENU                      # # # # # # # # # # # # 
######################################################################################
######################################################################################

class Scatter_PT_ScatteringPanel(bpy.types.Panel):
    bl_idname = "Scatter_PT_ScatteringPanel"
    bl_label = "Scatter"
    bl_category = "Scatter"
    bl_space_type = "VIEW_3D"
    bl_region_type = "UI"

    def draw(self, context):
        layout = self.layout
        bigbutton = layout.column()
        bigbutton.scale_y=2
        bigbutton.operator('scatter.custom01', text="SCATTER",  icon='PLAY')

######################################################################################
######################################################################################
# # # # # # # # # # # #                 REG                    # # # # # # # # # # # # 
######################################################################################
######################################################################################

def register():
    bpy.utils.register_class(ScatterPref)
    bpy.utils.register_class(Scatter_OT_Custom01)
    bpy.utils.register_class(Scatter_PT_ScatteringPanel)

def unregister():
    bpy.utils.unregister_class(ScatterPref)
    bpy.utils.unregister_class(Scatter_OT_Custom01)
    bpy.utils.unregister_class(Scatter_PT_ScatteringPanel)


if __name__ == "__main__":
    register()
    #write ops to do on script exec

Scatter_v01.py (17.3 KB)

@filibis i don’t think thoses are true particles ? are they ?

2 Likes

Does anybody know if I purchased the Addon I get free upgrades, or is it just a one time thing?

Yes, you get all updates for free

Cool thank u.