WarThunderCDK blender_plugins

Список разделов Blender 3D Плагины

Описание: Обсуждение расширений для Blender
Модератор: exooman

Сообщение #1 --help » 16.02.2017, 10:44

Доброго времени суток народ.
Балуюсь с WarThunderCDK.
В пакете 3 проги. Смотрелка игравых элементов, редактор локацый, редактор миссий.
Проект заточен под 3Dmax, и там целая куча папочек с "плагинами" под разные версии максов.
Собственно этот плагин добавляет в интерфейс макса всякое нужное. Экспорт файлов, свои шейдеры материалы и много чего еще.
Как я понимаю, максовский плагин переделать под блендер сложно. Нужно знать тонкости его работы.
Спойлер
http://stackoverflow.com/questions/32172118/3ds-max-plugin-to-blender-plugin
Но рядом с максовыми плагинами есть папочка с названием blender_plugins.
В папке 4 файлика.

1- __init__.py -сам скрипт

2- blk2dag.exe какая-то программка, видимо нужна для работы скрипта

3- config.blk какойто конфиг. "*.blk" текстовые файлики проекта, обычно местные конфиги

4- readme.txt инструкция по установке и "использованию".

В редмишке сказано что куда копировать и как использовать.
readme.txt
Спойлер
Copy whole this folder to blender ("...\Blender Foundation\Blender\2.71\scripts\addons\io_export_dag" )


1. В параметрах текстуры (custom properties) должен быть указан ее тип (параметр - type, значение из dagorShaders.cfg - "diffuse", "CUBE environment", "normalmap" и т.п.
2. В параметрах материала (custom properties) казываются необходимые параметры для его настройки, согласно dagorShaders.cfg

"Местный" конфиг config.blk
Спойлер
mat {
name:t="simple"
class:t="simple_aces"
real_two_sided:b=no
atest:i=127
map_diffuse:i=0
}

mat {
name:t="fence"
class:t="simple_aces"
atest:i=64
lighting:t="lightmap"
real_two_sided:b=yes
map_diffuse:i=0
map_normal:i=2
}

Сам скрипт __init__.py
Спойлер
# http://live.warthunder.com/assistance_agreement/

bl_info = {"name": "Export to Dagor Engine",
"description": "Script exports mesh to Dagor Engine",
"author": "http://live.warthunder.com/assistance_agreement/",
"version": (1, 0),
"blender": (2, 68, 0),
"location": "File > Export",
"wiki_url": "",
"tracker_url": "http://live.warthunder.com/assistance_agreement/",
"category": "Import-Export"}



# Standard library imports
import sys
import os

# Blender imports
import bpy
from bpy_extras.io_utils import ImportHelper
from bpy.props import IntProperty, StringProperty
from bpy.types import AddonPreferences, Operator, Panel


import mathutils, math, struct
from mathutils import *

import subprocess


SUPPORTED_TYPES = ('MESH')#,'CURVE','EMPTY','TEXT','CAMERA','LAMP')

class BLKWriter:

file = None
ident = 0

def getIdent(self):
s = ""
for x in range(0, self.ident):
s += " "
return s

def writeHeader(self):
self.file.write('HEAD:t="BLK(float)"\n')
return

def open( self, filename ):
self.file = open(filename, "wt")
self.writeHeader()
return

def close( self ):
self.file.flush()
self.file.close()

def beginBlock(self, name):
self.file.write('\n%s%s{\n' % ( self.getIdent(), name ) )
self.ident += 1
return

def endBlock(self ):
self.ident -= 1
if self.ident < 0 : #assert
self.ident = 0
self.file.write('%s}\n' % self.getIdent() )
return

def writeStr(self, name, str):
self.file.write('%s%s:t="%s"\n' % ( self.getIdent(), name, str ) )
return

def writeInt(self, name, val):
self.file.write('%s%s:i=%d\n' % ( self.getIdent(), name, val ) )
return

def writeFloat(self, name, val):
self.file.write('%s%s:r=%g\n' % ( self.getIdent(), name, val ) )
return

def writeBool(self, name, val):
self.file.write('%s%s:b=%s\n' % ( self.getIdent(), name, "yes" if val else "no" ) )
return

def writeTM(self, name, m):
self.file.write('%s%s:m=[[%f, %f, %f] [%f, %f, %f] [%f, %f, %f] [%f, %f, %f]]\n' % ( self.getIdent(), name, m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2], m[2][0], m[2][1], m[2][2], m[3][0], m[3][1], m[3][2] ) )

#self.file.write('%s%s:m=[[%f, %f, %f] [%f, %f, %f] [%f, %f, %f] [%f, %f, %f]]\n' % ( self.getIdent(), name, m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2], m[2][0], m[2][1], m[2][2], m[0][3], m[1][3], m[2][3] ) )
#self.file.write('%s%s:m=[[%f, %f, %f] [%f, %f, %f] [%f, %f, %f] [%f, %f, %f]]\n' % ( self.getIdent(), name, m[0][0], m[2][0], m[1][0], m[0][1], m[2][1], m[1][1], m[0][2], m[2][2], m[1][2], m[0][3], m[2][3], m[1][3] ) )
#self.file.write('%s\n' % m )
return



class MaterialExp:

def __init__(self):
self.index = -1
self.mat = None

class MeshExp:

def __init__(self):
self.vertexes = list()
self.faces = list()
self.uv = list()
self.normals = list()

self.normals_per_face_ver = list()

self.normals_ver = list()
self.normals_faces = list()

def addNormal( self, normal ):
for i,n in enumerate(self.normals_ver):
if n == normal:
return i
index = len( self.normals_ver )
self.normals_ver.append( normal )
return index




class NodeExp:
def __init__(self):
self.name = ""
self.id = -1
self.parent_id = -1
#self.parent = None
self.child = list()
self.mesh = None
self.tm = None
self.wtm = None
self.renderable = True



class DagExporter(Operator, ImportHelper):
bl_idname = "dag.exporter"
bl_label = "Dagor Engine (.dag)"
bl_options = {'REGISTER', 'UNDO'}

file = None

nodes = None
last_index = 0
writer = None

materials = {}

def __init__(self):
self.writer = BLKWriter()
self.last_index = 0
self.nodes = list()
self.materials = {}


# ImportHelper mixin class uses this
filename_ext = ".dag"
filter_glob = StringProperty(default="*.dag", options={'HIDDEN'})


def createRootNode( self ):
node = NodeExp()
node.id = 65535;
node.parent_id = -1;
node.tm = Matrix()
node.tm.identity()
node.wtm = Matrix()
node.wtm.identity()
node.renderable=False
self.nodes.append( node )
return node

def writeNode(self, node ):
self.writer.beginBlock("node")
self.writer.writeStr( "name", node.name )
self.writer.writeInt( "id", node.id )
self.writer.writeInt( "parent_id", node.parent_id )
self.writer.writeBool( "renderable", node.renderable )
self.writer.writeBool( "castshadow", False )
self.writer.writeBool( "rcvshadow", False )
self.writer.writeTM( "tm", node.tm )

if node.mesh == None:
self.writer.endBlock()
return

self.writer.beginBlock("vert_data")
for i,v in enumerate( node.mesh.vertexes):
self.writer.writeStr( "vert", str( '[%d] x:%f y:%f z:%f' % (i, v.x, v.y, v.z ) ) );
self.writer.endBlock()

self.writer.beginBlock("face_data")
for i,f in enumerate(node.mesh.faces):
self.writer.writeStr( "face", str( '[%d] i1:%d i2:%d i3:%d smgrp:%d mat: %d' % (i, f[ 0 ], f[ 1 ], f[ 2 ], 0, f[ 3 ] ) ) );
self.writer.endBlock()

self.writer.beginBlock("normal_vert")
for i,n in enumerate(node.mesh.normals_ver):
self.writer.writeStr( "nvert", str( '[%d] x:%f y:%f z:%f' % (i, n[ 0 ], n[ 1 ], n[ 2 ] ) ) );
self.writer.endBlock()

self.writer.beginBlock("normal_faces")
for i,f in enumerate(node.mesh.normals_faces):
self.writer.writeStr( "nface", str( '[%d] i1:%d i2:%d i3:%d' % (i, f[ 0 ], f[ 1 ], f[ 2 ] ) ) );
self.writer.endBlock()


if len(node.mesh.uv) > 0:
self.writer.beginBlock("uv_data")
for channel in node.mesh.uv:
#self.writer.writeStr( str('%s\n' % channel ) );
self.writer.beginBlock("channel")
self.writer.beginBlock("uv_vert")
for i,f in enumerate(channel):
self.writer.writeStr( "uv", str( '[%d] x:%f y:%f' % (i, f[0],1.0-f[1] ) ) );
#self.writer.writeStr( str('%s\n' % ch ) );
self.writer.endBlock()

self.writer.endBlock()

self.writer.endBlock()

self.writer.endBlock()
return


def dumpObjectData( self, obj, node, scene ):

#mesh_calc_normals()

if obj.type != 'MESH':
try:
me = obj.to_mesh(scene, True, "PREVIEW")
except:
me = None
is_tmp_mesh = True
else:
me = obj.data
if not me.tessfaces and me.polygons:
me.calc_tessface()
is_tmp_mesh = False



has_uv = bool(me.tessface_uv_textures)
has_vcol = bool(me.tessface_vertex_colors)
if has_uv == False :
self.file.write("Object hasn't UV mapping\n" )

# export data
if me is not None:

if len( obj.material_slots ) == 0:
print( "Need assign material to object \"%s\"\n" % obj.name )
return


me.calc_normals_split()

materials_indexes = [ list() for p in range( len( obj.material_slots ) )]

for i,slot in enumerate( obj.material_slots ):
self.file.write(" Material %s\n" % slot )

#self.file.write(" Mat %s\n" % slot.material.name )

if slot.material.name not in self.materials:
mat_exp = MaterialExp()
mat_exp.index = len(self.materials)
mat_exp.mat = slot.material
self.materials[ slot.material.name ] = mat_exp


val = self.materials.get( slot.material.name )
if val != None:
materials_indexes[i] = val.index
else:
materials_indexes[i] = -1


#print("Mesh object:", me.name)
self.file.write(str('mesh %s\n' % me) )
meshExp = MeshExp()


for v in me.vertices:
#self.file.write(str('vert %s %s %s\n' % (v.co.x, v.co.y, v.co.z) ) )
meshExp.vertexes.append( v.co )
meshExp.normals.append( -v.normal );

meshExp.uv = [ list() for p in range( len( me.tessface_uv_textures ) )]


for i, f in enumerate( me.tessfaces ):
#self.file.write(str('vf %s %s %s\n' % (f.vertices[0], f.vertices[1], f.vertices[2] ) ) )

useSmooth = f.use_smooth

self.file.write(str('mat %d\n' % f.material_index ) )
fexp = []
fexp.append( f.vertices[0] )
fexp.append( f.vertices[2] )
fexp.append( f.vertices[1] )
fexp.append( materials_indexes[ f.material_index ] );
meshExp.faces.append( fexp )


norm = []

if useSmooth:
norm.append( -meshExp.normals[ f.vertices[0] ] )
norm.append( -meshExp.normals[ f.vertices[2] ] )
norm.append( -meshExp.normals[ f.vertices[1] ] )
else:
norm.append( f.normal )
norm.append( f.normal )
norm.append( f.normal )

meshExp.normals_per_face_ver.append( norm )


norm_face_ind = []
for nv in norm:
norm_face_ind.append( meshExp.addNormal( nv ) )
meshExp.normals_faces.append( norm_face_ind )



for n, layer in enumerate( me.tessface_uv_textures ):
uv = layer.data[i].uv
#self.file.write(str('uv_face[%d] (%f,%f), (%f,%f), (%f,%f)\n' % (n, uv[0][0], uv[0][1],uv[1][0], uv[1][0],uv[2][0],uv[2][1]) ) )
meshExp.uv[ n ].append( uv[0] )
meshExp.uv[ n ].append( uv[2] )
meshExp.uv[ n ].append( uv[1] )
if len(f.vertices) == 4: # if quad
meshExp.uv[ n ].append( uv[2] )
meshExp.uv[ n ].append( uv[0] )
meshExp.uv[ n ].append( uv[3] )

self.file.write(str('smth %s\n' % f.use_smooth ) )


if len(f.vertices) == 4: # if quad
self.file.write(str('vf %s %s %s\n' % (f.vertices[2], f.vertices[3], f.vertices[0] ) ) )
fexp = []
fexp.append( f.vertices[2] )
fexp.append( f.vertices[0] )
fexp.append( f.vertices[3] )
fexp.append( materials_indexes[f.material_index] );
meshExp.faces.append( fexp )
#self.file.write(str('uv_face (%f,%f), (%f,%f), (%f,%f)\n' % (uv[2][0], uv[2][1],uv[3][0], uv[3][0],uv[0][0],uv[0][1]) ) )

norm = []
if useSmooth:
norm.append( -meshExp.normals[ f.vertices[2] ] )
norm.append( -meshExp.normals[ f.vertices[0] ] )
norm.append( -meshExp.normals[ f.vertices[3] ] )
else:
norm.append( f.normal )
norm.append( f.normal )
norm.append( f.normal )

meshExp.normals_per_face_ver.append( norm )

norm_face_ind = []
for nv in norm:
norm_face_ind.append( meshExp.addNormal( nv ) )
meshExp.normals_faces.append( norm_face_ind )


node.mesh = meshExp

if is_tmp_mesh:
bpy.data.meshes.remove(me)
return

def recursObjects( self, objs, parent, scene):
objects = (ob for ob in objs if ob.is_visible(scene) and ob.type in SUPPORTED_TYPES)

for o in objects:
self.last_index += 1;

node = NodeExp()
node.name = o.name
node.id = self.last_index;
node.parent_id = parent.id

mParentInv = parent.wtm.copy()
mParentInv.invert()

node.wtm = o.matrix_world;
node.tm = mParentInv * node.wtm;

#--------------------------------------
if parent.parent_id == -1 :
tmp_r = node.tm[1].copy()
node.tm[1] = node.tm[2].copy()
node.tm[2] = tmp_r.copy()
node.tm.transpose()


#self.file.write( str( '(%s) mat: %s\n' % ( node.name, node.tm ) ) )

#--------------------------------------

#if o.name == 'gear_c' :
# self.file.write( str( 'mat: %s\n' % o.matrix_world ) )

parent.child.append( node )

print('Exporting %s, parent%s\n' % ( node.name, parent.name) )
self.nodes.append( node )

self.dumpObjectData( o, node, scene )
self.recursObjects( o.children, node, scene )

return


def writeParam(self,params):
for key in params:
if key[0] == "_RNA_UI":
continue
self.writer.beginBlock( "param")
self.writer.writeStr( "name", key[0] )
self.writer.writeStr( "value", key[1] )
self.writer.endBlock()


def writeMaterail(self, mat, id):
self.writer.beginBlock( "material")
self.writer.writeInt( "id", id )
self.writer.writeStr( "name", mat.name )
self.writer.writeStr( "diffuse", str( 'r:%f g:%f b:%f' % (mat.diffuse_color.r, mat.diffuse_color.g, mat.diffuse_color.b)) )
self.writer.writeStr( "specular", str( 'r:%f g:%f b:%f' % (mat.specular_color.r, mat.specular_color.g, mat.specular_color.b)) )
for mtex_slot in mat.texture_slots:
if mtex_slot:

#self.file.write( "test: %s" % str( mtex_slot.uv_layer ) )

if hasattr(mtex_slot.texture , 'image'):
self.writer.beginBlock( "texture" )
self.writer.writeStr( "filepath", mtex_slot.texture.image.filepath )
self.writeParam( mtex_slot.texture.items() )
self.writer.endBlock()


self.writeParam( mat.items() )
self.writer.endBlock()



def writeBlk(self, filename):
self.writer.open( filename )

for n in self.nodes:
self.writeNode( n )

for key, value in self.materials.items():
self.writeMaterail( value.mat, value.index )
print(key, value)

self.writer.close()
return

def execute(self, context):

filepath = self.filepath
filepath = bpy.path.ensure_ext(filepath, self.filename_ext)

print("Blender Version {}.{}.{}".format(bpy.app.version[0], bpy.app.version[1], bpy.app.version[2]))
print("Filepath: {}".format(filepath))

scene = context.scene

self.file = open(filepath+".log", "wt")


objs = (ob for ob in scene.objects if ob.is_visible(scene) and ob.type in SUPPORTED_TYPES and ob.parent == None)
par_node = self.createRootNode()

self.recursObjects( objs, par_node, scene )



path_blk = filepath + ".blk"
print( 'path_blk=%s' % path_blk );
self.writeBlk( path_blk )

prev_path = os.getcwd()
new_path = os.path.dirname( os.path.realpath(__file__) )
os.chdir( new_path )

print(new_path);

try:
return_code = subprocess.call(["blk2dag.exe", path_blk, filepath ])
print( 'ret=%d' % return_code );
except IOError:
print( 'convert failed' );

os.chdir( prev_path )

self.file.flush()
self.file.close()


return {'FINISHED'}


def menu_func_import(self, context):
self.layout.operator(DagExporter.bl_idname,text=DagExporter.bl_label)

def register():
bpy.utils.register_module(__name__)
bpy.types.INFO_MT_file_export.append(menu_func_import)

def unregister():
bpy.utils.unregister_module(__name__)
bpy.types.INFO_MT_file_export.remove(menu_func_import)

if __name__ == "__main__":
register()

Что и куда нужно вписать в custom properties

Сам dagorShaders.cfg есть в почти каждой папке с максовыми плагинами. От простого для "старых максов"
и помудренее для свежих.
dagorShaders.cfg
Спойлер
[_settings]
tex1_name="diffuse"
tex2_name="CUBE environment"
tex3_name="normalmap"
tex4_name="parallax (height field)"
tex5_name="glossmap (specular mask+shininess)"
tex6_name="emission tex (unsupported!)"
tex7_name="lightmap"
tex8_name="detail tex"

[_global_params]
lighting=enum(none lightmap vltmap)
real_two_sided=enum(yes no)
scene_emission=real optional
emission=real optional
relief = real optional
atest=text optional
reflection_multiplier=int optional


;Aces
[aces_weapon_fire]
atest=int optional
zbias=real optional

[collimator]

[dynamic_alpha_blend]
atest=text optional
zbias=real optional

[dynamic_facing_leaves]
atest=int optional
normal_offset=real optional
impostor=int optional

[dynamic_glass_chrome]
min_reflect=real optional
max_reflect=real optional
fresnel_power=real optional
opacity=real optional

[dynamic_illum]
atest=int optional
ablend=int optional

[dynamic_layered]
//тайлы детейлов:
detail1_tile_u=real optional
detail1_tile_v=real optional
detail2_tile_u=real optional
detail2_tile_v=real optional

detail1_scroll_u=text optional
detail1_scroll_v=text optional
detail2_scroll_u=text optional
detail2_scroll_v=text optional

//тайл атласа оверлея:
atlas_tile_u=real optional
atlas_tile_v=real optional
atlas_first_tile=real optional
atlas_last_tile=real optional

//тайл маски:
mask_tile_u=real optional
mask_tile_v=real optional

//скроллить или нет маску от положения (может быть 0 или 1):
mask_scroll_u=text optional
mask_scroll_v=text optional

//рандомизованная гамма ("от" .. "до"):
mask_gamma_start=real optional
mask_gamma_end=real optional

//2nd detail recolour
detail2_colored=text optional

//горизонтальное проецирование второго детейла сверху на объект:
top_projection=text optional

//углы проецирования:
top_projection_from=real optional
top_projection_to=real optional

//уровень влияния оверлая на второй детрейл при проецировании:
top_projection_detail2_modulate=real optional

//покраска 1го или 2го детейла текстурой покраски
use_painting=real optional
painting_line=real optional
paint1stdetail=real optional

//смешиваем нормали детейлов, где преобладает 2й детейл
detail2_combined_normal=real optional


[dynamic_masked_chrome]
min_reflect=real optional
max_reflect=real optional
atest=int optional
use_decal_tex=int optional
lit_with_gun_fire=int optional

[dynamic_masked_chrome_bump]
not_use_fxaa=int optional
is_outer_cockpit=int optional
min_reflect=real optional
max_reflect=real optional
fresnel_power=real optional
sun_reflection_power=real optional
atest=int optional
use_decal_tex=int optional
texcoord_mulp=int optional
texcoord_anim=int optional
lit_with_gun_fire=int optional
//покраска модели текстурой покраски
use_painting=real optional
painting_line=real optional


[dynamic_masked_glass_chrome]
min_reflect=real optional
max_reflect=real optional
fresnel_power=real optional
zbias=real optional
slope_zbias=real optional
opacity=real optional
rain_droplets=real optional

[dynamic_masked_ship]
detail_scale_x=real optional
detail_scale_y=real optional
detail_distinctness=real optional
camo_gamma_mul=real optional
atest=int optional

[dynamic_skin]
[dynamic_masked_tank]

[dynamic_mirror]

[dynamic_null]

[dynamic_pbr_glass]

[dynamic_simple]
atest=text optional
lit_with_gun_fire=int optional

[dynamic_simple_colored]
atest=text optional
zbias=real optional
slope_zbias=real optional

[dynamic_tank_atest]
texcoord_anim=int optional

[dynamic_tank_foliage]
atest=int optional

[dynamic_tank_selfillum]

[dynamic_tele]

[dynamic_translucency]
atest=int optional

[envi]

[gi_black]

[gyro_sight]

[glass_crack]

[hatching]

[lake]
water_move_period=real optional
water_dir_u=real optional
water_dir_v=real optional
water_x_scale=real optional
water_y_scale=real optional
water_min_reflect=real optional
water_max_reflect=real optional
water_height=real optional
;water_height_speed=real optional
;water_tex_rotate=real optional

[land_mesh_combined]
big=int optional
should_use_detail=real optional
bake_landmesh_combined=int optional
render_landmesh_combined=int optional
vertical_from_geom_norm=int optional

[land_mesh_combined_normal]

[land_mesh_height_decal]

[layered_static]
detail1_tile_u=real optional
detail1_tile_v=real optional
detail2_tile_u=real optional
detail2_tile_v=real optional

[layered_hangar]
reflection_multiplier=real optional
detail1_tile_u=real optional
detail1_tile_v=real optional
detail2_tile_u=real optional
detail2_tile_v=real optional

[masked_glass_chrome]
opacity=real optional

[propeller_front]

[propeller_helicopter]

[propeller_helicopter_opaque]

[propeller_side]

[random_grass]


[rendinst_layered]
//тайлы детейлов:
detail1_tile_u=real optional
detail1_tile_v=real optional
detail2_tile_u=real optional
detail2_tile_v=real optional

detail1_scroll_u=text optional
detail1_scroll_v=text optional
detail2_scroll_u=text optional
detail2_scroll_v=text optional

//тайл атласа оверлея:
atlas_tile_u=real optional
atlas_tile_v=real optional
atlas_first_tile=real optional
atlas_last_tile=real optional

//тайл маски:
mask_tile_u=real optional
mask_tile_v=real optional

//скроллить или нет маску от положения (может быть 0 или 1):
mask_scroll_u=text optional
mask_scroll_v=text optional

//рандомизованная гамма ("от" .. "до"):
mask_gamma_start=real optional
mask_gamma_end=real optional

//трава
draw_grass=text optional

//2nd detail recolour
detail2_colored=text optional

//горизонтальное проецирование второго детейла сверху на объект:
top_projection=text optional

//углы проецирования:
top_projection_from=real optional
top_projection_to=real optional

//уровень влияния оверлая на второй детрейл при проецировании:
top_projection_detail2_modulate=real optional

//покраска 1го детейла текстурой покраски
use_painting=real optional
painting_line=real optional
paint1stdetail=real optional

//смешиваем нормали детейлов, где преобладает 2й детейл
detail2_combined_normal=real optional

[rendinst_simple_emissive]

[simple_aces]
atest=text optional
detail_scale_u=real optional
detail_scale_v=real optional

//покраска модели текстурой покраски
use_painting=real optional
painting_line=real optional

[simple_aces_detailed]

[simple_bump]
atest=text optional
zbias=real optional
slope_zbias=real optional
specularStr=real optional
specularPow=real optional
relief=real optional

[simple_hangar]
detail1_tile_u=real optional
detail1_tile_v=real optional
detail2_tile_u=real optional
detail2_tile_v=real optional

[tracer_head]

Собственно на этом и закавыка. Модель експортируется но она черная.
Как я понимаю в менюшке custom properties создается некий блок/ссылка. Или что-то, что помогает аддону приделать шейдер из максового плагина.
В моем случае нет то-ли материала, то-ли текстуры. Пробовал в максе экспортировать без материала или с материалам но без текстуры, модель получается черной.

Разработчики должны понимать как работает их плагин для макаса и способны смастерить рабочий аддон для блендера. И логичнее спрашивать на их сайте. Пробовал, спрашивал. Создал две ветки. На русскоязычном форуме и на англоязычном. Пока разработчики молчат а темы собирают просмотры :oD


Подскажите как заставить работать этотот аддон/плагин.
$ telnet towel.blinkenlights.nl
--help
Аватара
Сообщения: 62

Вернуться в Плагины

Кто сейчас на форуме (по активности за 5 минут)

Сейчас этот раздел просматривают: 2 гостя

cron