CSV Exporter

Forums: CSV Exporter
Blender (Export script developed by yoyoz; Integrated Plugin developed by JimStar)

CSV Exporter

Postby matej » Fri Apr 04, 2014 5:36 pm

matej Fri Apr 04, 2014 5:36 pm
This is a small utility tool to export instance transforms to a .csv file to be used inside standalone Octane. There is the other instances exporter script that needs the unofficial Lionel's exporter plugin to work properly and has some additional functionality (it exports the base particle mesh for you)

This one just creates a .csv and nothing else. You have to export the appropriate particle meshes yourself.
csvexporter.png
csvexporter.png (8.96 KiB) Viewed 9149 times


Select your emitter in the 3D window, choose the location where to save files, choose the up axis (Octane has Y up, which is the default selection) and hit Export transforms. Note that the button will be greyed out, if the active selected object in the scene is not a duplicator.

So, what's a "duplicator":

* An emitter that has one or more particle systems assigned. The exported .csv file will have the name of the PS. Works for hair and emitter. Only "object visualization" is supported.

* An emitter that has dupliverts / duplifaces enabled. The .csv file will have the name of the emitter object.

* Any object that has a shared mesh datablock. This means that you can Alt+D duplicate your objects, select one (any) of them and it will export the object datablock transforms for all of them that share this mesh. The .csv file will have the name of the mesh.

IMPORTANT: When you are saving your particle mesh (obj/mtl combo), it must be centred at Blender world origin, because Octane treats .csv data as absolute global positions.

It installs just like any addon. Its located in the import-export tab and shows up in the Toolbar on the left side of the 3D window.

Hope it will be useful to some of you :D
Attachments
csvexporter_v101.zip
(3.46 KiB) Downloaded 812 times
Last edited by matej on Mon Apr 07, 2014 3:51 pm, edited 1 time in total.
SW: Octane 3.05 | Linux Mint 18.1 64bit | Blender 2.78 HW: EVGA GTX 1070 | i5 2500K | 16GB RAM Drivers: 375.26
cgmo.net
User avatar
matej
Licensed Customer
Licensed Customer
 
Posts: 2083
Joined: Fri Jun 25, 2010 7:54 pm
Location: Slovenia

Re: CSV Exporter

Postby matej » Fri Apr 04, 2014 5:49 pm

matej Fri Apr 04, 2014 5:49 pm
A visual reminder how to setup instances in Octane (if anoyne forgot :D )
setup.jpg

The scene has one emitter with two PS (one hair and one emitter type). Each PS uses a different object for visualization.

Cheers!
SW: Octane 3.05 | Linux Mint 18.1 64bit | Blender 2.78 HW: EVGA GTX 1070 | i5 2500K | 16GB RAM Drivers: 375.26
cgmo.net
User avatar
matej
Licensed Customer
Licensed Customer
 
Posts: 2083
Joined: Fri Jun 25, 2010 7:54 pm
Location: Slovenia

Re: CSV Exporter

Postby ROUBAL » Mon Apr 07, 2014 3:44 pm

ROUBAL Mon Apr 07, 2014 3:44 pm
Great work. I am happy of it. Installed and works well in Blender official and Blender for Octane.
Thank you very much ! :P
French Blender user - CPU : intel Quad QX9650 at 3GHz - 8GB of RAM - Windows 7 Pro 64 bits. Display GPU : GeForce GTX 480 (2 Samsung 2443BW-1920x1600 monitors). External GPUs : two EVGA GTX 580 3GB in a Cubix GPU-Xpander Pro 2. NVidia Driver : 368.22.
User avatar
ROUBAL
Licensed Customer
Licensed Customer
 
Posts: 2199
Joined: Mon Jan 25, 2010 5:25 pm
Location: FRANCE

Re: CSV Exporter

Postby matej » Mon Apr 07, 2014 3:52 pm

matej Mon Apr 07, 2014 3:52 pm
I've updated the top post with a new version that fixes two issues wit hair emitter (details in the other thread)
SW: Octane 3.05 | Linux Mint 18.1 64bit | Blender 2.78 HW: EVGA GTX 1070 | i5 2500K | 16GB RAM Drivers: 375.26
cgmo.net
User avatar
matej
Licensed Customer
Licensed Customer
 
Posts: 2083
Joined: Fri Jun 25, 2010 7:54 pm
Location: Slovenia

Re: CSV Exporter

Postby ROUBAL » Mon Apr 07, 2014 4:29 pm

ROUBAL Mon Apr 07, 2014 4:29 pm
Thanks a lot.

Like I did last time, I have done few cosmetical tweaks (that's all I am able to do :D ) for my own usage : Changed to the render panel to have it with my other exporters and added version number to avoid confusion if reinstall is needed.
As you may have some good reasons to disable the version number, I wrote it in the label itself.
If at one moment you have to update the script again for any reason, it could be useful to make the version number appear in the GUI :
Attachments
csvexporter-v101.jpg
French Blender user - CPU : intel Quad QX9650 at 3GHz - 8GB of RAM - Windows 7 Pro 64 bits. Display GPU : GeForce GTX 480 (2 Samsung 2443BW-1920x1600 monitors). External GPUs : two EVGA GTX 580 3GB in a Cubix GPU-Xpander Pro 2. NVidia Driver : 368.22.
User avatar
ROUBAL
Licensed Customer
Licensed Customer
 
Posts: 2199
Joined: Mon Jan 25, 2010 5:25 pm
Location: FRANCE

Re: CSV Exporter

Postby pegot » Tue Dec 27, 2016 4:10 pm

pegot Tue Dec 27, 2016 4:10 pm
Thanks - this script works perfectly! Is there any way to have it generate a series of CSV files for each frame of a Blender animation? Then we could feed those files into the animation feature of Octane SA’s scatter node.

I tested this with a few manually generated CSV files on different frames in Blender and it works great when brought into Octane. But I have no idea how to tell Blender to automate such an export for all frames of an animation.
Win 10
3.7Ghz i9 10900k / 64GB
ASUS STRIX Z490-E
PSU: PowerSpec 850Wd
RTX 3090 Asus Tuff

Network rendering:
Win 10
4.2Ghz i7 7700k / 64GB
AsRock SuperCarrier
PSU: EVGA 1200w
RTX 3080 Ti EVGA Hybrid
RTX 3080 ASUS Tuff
GTX 1080ti SC Black (wc)
pegot
Licensed Customer
Licensed Customer
 
Posts: 921
Joined: Mon Nov 07, 2011 3:44 am

Re: CSV Exporter

Postby fivebythree » Wed Apr 19, 2017 5:53 pm

fivebythree Wed Apr 19, 2017 5:53 pm
Perfect! Thank you for making the plugin.
STANDALONE WINDOWS10PRO | RTX3090 | XEON 4210 20 CORE | 96GBRam
User avatar
fivebythree
Licensed Customer
Licensed Customer
 
Posts: 230
Joined: Tue Jun 10, 2014 2:43 pm
Location: Toronto, CAN

Re: CSV Exporter

Postby christophritler » Fri Dec 15, 2017 12:23 pm

christophritler Fri Dec 15, 2017 12:23 pm
This Plugin would be perfect, but I can't get it to work properly.

Blender Version is 2.76

When I hit the export button, the plugin tells me that it was exported correctly,
but there is no .csv file in the specified folder.

What am I doing wrong? :|
christophritler
Licensed Customer
Licensed Customer
 
Posts: 3
Joined: Mon Mar 23, 2015 3:16 pm

Re: CSV Exporter

Postby regionfive » Fri Jan 12, 2018 9:26 am

regionfive Fri Jan 12, 2018 9:26 am
you rock! :D
regionfive
Licensed Customer
Licensed Customer
 
Posts: 73
Joined: Mon Dec 06, 2010 5:50 pm
Location: Nuremberg/Germany

Re: CSV Exporter

Postby Issa » Sat Jul 21, 2018 9:52 pm

Issa Sat Jul 21, 2018 9:52 pm
Hi,
I made a little change to allow animation export

Code: Select all
#!/usr/bin/python
# -*- coding: utf-8 -*-

# ##### BEGIN GPL LICENSE BLOCK #####
#
#  This program is free software; you can redistribute it and/or
#  modify it under the terms of the GNU General Public License
#  as published by the Free Software Foundation; either version 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, write to the Free Software Foundation,
#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
bl_info = {
   "name": "CSV instance transform exporter",
   "author": "Matej Moderc, Issa",
   "version": (1,0,1),
   "blender": (2, 6, 3),
   "location": "Toolbar",
   "description": "Writes a .csv file containing instance transforms of selected duplicator object",
   "warning": "testing",
   "wiki_url": "",
   "tracker_url": "",
   "category": "Import-Export"}

from math import radians

import os
import bpy
import bpy.path as bpath
from bpy.types import Operator, Panel, PropertyGroup
from bpy.props import StringProperty, EnumProperty, PointerProperty
from mathutils import Matrix, Quaternion, Vector

VERSION = ".".join(str(v) for v in bl_info["version"])

def info(msg):
   print("INFO: %s" % msg)
def warning(msg):
   print("WARNING: %s" % msg)
def error(msg):
   print("ERROR: %s" % msg)

class CSVExportException(Exception):
   def __init__(self, msg):
      self._msg = msg
   def __str__(self):
      return repr(self._msg)

########################
#   EXPORT FUNCTIONS   #
########################

def writeTransform(t, fh):
   """Writes first three matrix rows to the file handle"""   
   vals = ["%.6f" % v for v in tuple(t[0]) + tuple(t[1]) + tuple(t[2])]
   fh.write(" ".join(vals) + "\n")

def exportDuplis(context, emitter, t_world, t_world_inv):
   """Exports dupli objects (dupliverts / duplifaces) for the specified emitter"""
   info("Exporting dupli objects for '%s'" % emitter.name)
   export_dir = bpath.abspath(context.scene.csv_exporter_options.export_dir)
   emitter.dupli_list_create(context.scene)
   lst = emitter.dupli_list
   if len(lst) == 0: # if not on active layers, dupli list = empty
      return
   try:
      fh = open(export_dir + emitter.name + ".csv", "w")
      for duplicate in lst.values():
         t = duplicate.matrix.copy()
         writeTransform(t_world * t * t_world_inv, fh)
      fh.close()
   except IOError as err:
      msg = "IOError during file handling '{0}'".format(err)
      error(msg)
      raise CSVExportException(msg)
   emitter.dupli_list_clear()

def exportMeshDuplis(context, obj, users, t_world, t_world_inv):
   """Exports the transforms of Alt+D duplicated objects"""
   export_dir = bpath.abspath(context.scene.csv_exporter_options.export_dir)
   csv_filename = export_dir + obj.data.name + ".csv"
   info("Saving transforms for '%s' mesh-dupli objects into file '%s' " % (obj.data.name, csv_filename))
   
   try:
      fh = open(csv_filename, "w")
      for o in users:
         t = o.matrix_world.copy()
         writeTransform(t_world * t * t_world_inv, fh)
      fh.close()
   except IOError as err:
      msg = "IOError during file handling '{0}'".format(err)
      error(msg)
      raise CSVExportException(msg)

def exportParticles(context, emitter, psys, t_world, t_world_inv):
   """Exports the particle system transforms for the specified emitter"""
   export_dir = bpath.abspath(context.scene.csv_exporter_options.export_dir)
   pset = psys.settings
   infostr = "Exporting PS '%s' (%s) on emitter '%s'" % (psys.name, pset.type, emitter.name)
   particles = [p for p in psys.particles] if pset.type == 'HAIR' else [p for p in psys.particles if p.alive_state == 'ALIVE']
   
   if pset.render_type != "OBJECT":
      warning("Invalid PS visualization type '%s'" % pset.render_type)
      return
   if not pset.use_rotation_dupli:
      warning("'Use object rotation' should be on. Rotations wont conform to Blender veiwport")
   
   try:
      fh = open(export_dir + psys.name + ".csv", "w")
      for p in particles:
         #if pset.type == 'HAIR' or not p.alive_state == 'DEAD':
         rot = Quaternion.to_matrix(p.rotation).to_4x4()
         if (pset.type == "HAIR"):
            h1 = p.hair_keys[0].co
            h2 = p.hair_keys[-1].co
            loc = Matrix.Translation(h1)
            scale = Matrix.Scale((h2 - h1).length, 4)
            emitter_rot = emitter.matrix_world.decompose()[1].to_matrix().to_4x4()   
            rot = emitter_rot.inverted() * rot
         else:
            loc = Matrix.Translation(p.location.copy())
            scale = Matrix.Scale(p.size, 4)
         t = loc * rot * scale
         t = emitter.matrix_world * t if pset.type == "HAIR" else t
         writeTransform(t_world * t * t_world_inv, fh)
      fh.close()
   except IOError as err:
      msg = "IOError during file handling '{0}'".format(err)
      error(msg)
      raise CSVExportException(msg)

###################
#      GUI        #
###################

### GLOBAL OPTIONS OF THE EXPORTER ###
class CSVExporterOptions(PropertyGroup):

   export_dir = StringProperty(
      name = "",
      default = "/tmp",
      description = "Define the export path for the .csv file",
      subtype = "DIR_PATH"
      )

   world_space = EnumProperty(
      name="World space options",
      items=(('Y Up', 'Y Up', 'Writes transforms in Octane world space'),
            ('Z Up', 'Z Up', 'Writes transforms in Blender native space')),
      default='Y Up',
      #update=callback
   )

### EXPORTER PANEL ###
class CSVExporterPanel(Panel):
   """Exports instance transforms to a .csv file"""
   bl_space_type = "VIEW_3D"
   bl_region_type = "TOOLS"
   bl_label = "CSV Exporter" #+ VERSION

   def draw_header(self, context):
      layout = self.layout
      layout.label(text="", icon="PARTICLES")
 
   def draw(self, context):
      sce = context.scene
      opts = sce.csv_exporter_options
      layout = self.layout
      
      col = layout.column()
      col.label(text="Select export path")
      col.prop(opts, "export_dir")
      col.label(text="Select up axis")
      row = layout.row()
      row.prop(opts, "world_space", expand=True)

      col = layout.column(align=True)
      col.operator("ops.csv_write_transforms", icon="RENDER_STILL")

      col = layout.column(align=True)
      col.operator("ops.csv_write_animation", icon="RENDER_ANIMATION")

### WRITE TRANSFORMS BUTTON ###
class CSVWriteTransforms(Operator):
   """Writes instance transforms to .csv file. Note that a duplicator object must be selected."""
   bl_idname = "ops.csv_write_transforms"
   bl_label = "Export transforms"

   def execute(self, context):
      info("CSV instance transforms exporter")
      sce = context.scene
      with_warnings = False
      msg = None
      t_scale = Matrix.Scale(1, 4) #TODO: check for aplicable scaling
      t_world = Matrix.Rotation(radians(-90.0), 4, 'X') * t_scale
      t_world_inv = Matrix.Rotation(radians(90.0), 4, 'X') * t_scale.inverted()
      world_space_tag = sce.csv_exporter_options.world_space
      info("Exporting in %s world space" % (world_space_tag))
      if world_space_tag == "Z Up":
         t_world = Matrix.Identity(4)
         t_world_inv = Matrix.Identity(4)
      
      # utility func to send warnings through Blender GUI
      def report_warning(msg):
         self.report({'WARNING'}, msg)
         nonlocal with_warnings
         with_warnings = True

      ob = context.selected_objects[0]
      if ob.is_duplicator: # and obj.is_visible(sce) and not obj.hide_render:
         info("Found duplicator '%s' of type '%s'" % (ob.name, ob.dupli_type))
         if ob.dupli_type in ('FACES', 'VERTS'):
            exportDuplis(context, ob, t_world, t_world_inv)
         elif ob.dupli_type == 'NONE' and len(ob.particle_systems) > 0:
            # even if NONE for dupliverts, its possible PS
            if len(ob.particle_systems) > 0:   
               for ps in ob.particle_systems:
                  pset = ps.settings
                  if pset.render_type == "OBJECT":
                     exportParticles(context, ob, ps, t_world, t_world_inv)
                  else:
                     report_warning("Unsupported PS vizualisation type '%s'" % pset.render_type)
            else:
               report_warning("Object '%s' has no particle systems attached" % ob.name)
         else:
            report_warning("Unsupported duplicator type '%s'" % ob.dupli_type)
      # if not duplicator, possible dupliverts
      elif ob.type == "MESH" and ob.data.users > 1:
         # find all users of the mesh of this object, aka. duplicates
      #   meshes = bpy.data.meshes
         me = ob.data
         users = [o for o in bpy.data.objects if o.type == "MESH" and o.data == me]
         info("Found %i users for dupli-mesh '%s'" % (len(users), me.name))
         exportMeshDuplis(context, ob, users, t_world, t_world_inv)
      else:
         report_warning("The selected object '%s' is not an instance emitter or mesh-dupli" % ob.name)
      
      if with_warnings:
         msg = "Script ended with warnings, look into console"
         self.report({'WARNING'}, msg)
      else:
         msg = "Export complete. CSV file(s) written in '%s'" % (sce.csv_exporter_options.export_dir)
         info(msg)
         self.report({'INFO'}, msg)

      return {"FINISHED"}


   # button is active only when selected object is a duplicator
   @classmethod
   def poll(self, context):
      obs = context.selected_objects
      if len(obs) < 1:
         return False
      ob = obs[0]
      return ob.is_duplicator or (ob.type == "MESH" and ob.data.users > 1)

### WRITE ANIMATION BUTTON ###
class CSVWriteAnimation(Operator):
   """Writes instance transforms to .csv file. Note that a duplicator object must be selected."""
   bl_idname = "ops.csv_write_animation"
   bl_label = "Export animation"

   # button is active only when selected object is a duplicator
   @classmethod
   def poll(self, context):
      obs = context.selected_objects
      if len(obs) < 1:
         return False
      ob = obs[0]
      return ob.is_duplicator or (ob.type == "MESH" and ob.data.users > 1)

   def execute(self, context):

      sce = context.scene
      
      ###Export !
      curFilePath = sce.csv_exporter_options.export_dir
      if not os.path.isdir(curFilePath):
         curFilePath = os.path.split(curFilePath)[0]+"\\"

      RANGE = range(sce.frame_start, sce.frame_end+1)
      for frame in RANGE:
         #sce.frame_current =
         bpy.context.scene.frame_set(frame)
         bpy.data.particles.update()
         sce.update()
         sce.csv_exporter_options.export_dir = curFilePath + ("%06d" % (sce.frame_current,)) + "_"
         bpy.ops.ops.csv_write_transforms()
      ###

      return {"FINISHED"}



def register():
   bpy.utils.register_class(CSVWriteTransforms)
   bpy.utils.register_class(CSVWriteAnimation)
   bpy.utils.register_class(CSVExporterPanel)
   bpy.utils.register_class(CSVExporterOptions)
   #bpy.utils.register_module(__name__)
   bpy.types.Scene.csv_exporter_options = PointerProperty(type=CSVExporterOptions)

def unregister():
   bpy.utils.unregister_class(CSVWriteTransforms)
   bpy.utils.unregister_class(CSVWriteAnimation)
   bpy.utils.unregister_class(CSVExporterPanel)
   bpy.utils.unregister_class(CSVExporterOptions)
   #bpy.utils.unregister_module(__name__)
   del bpy.types.Scene.csv_exporter_options

if __name__=="__main__":
   register()
Issa
Licensed Customer
Licensed Customer
 
Posts: 26
Joined: Fri Feb 14, 2014 5:21 pm
Next

Return to Blender


Who is online

Users browsing this forum: No registered users and 5 guests

Thu Mar 28, 2024 4:20 pm [ UTC ]