1. Пользоваться форумом на планшетах и телефонах стало удобнее благодаря Tapatalk

Создание объёмного каркаса на Python

Тема в разделе "Blender3D", создана пользователем dmtr, 20 янв 2009.

Модераторы: logosman
  1. dmtr

    dmtr Пользователь сайта

    С нами с:
    06.10.2008
    Сообщения:
    6
    Симпатии:
    0
    Баллы:
    2
    Есть меш, который задаётся списком вершин и рёбер, которые соединяют их. Нужно рёбра сделать обьёмными, т.ё. что бы рёбра были представлены, например, Cylinder или Tube. Но они задаются:
    Код:
    Cylinder(verts=32, diameter=2.0, length=2.0)
    Tube(verts=32, diameter=2.0, length=2.0)
    Непонятно как их разместить вдоль ребра. Можно было бы сделать выдавливание окружности вдоль рёбер, но выдавливание работает только вдоль кривых. Какие ещё варианты могут быть?
     
  2. logosman vip

    logosman Moderator Команда форума

    С нами с:
    24.09.2005
    Сообщения:
    196
    Симпатии:
    15
    Баллы:
    366
    Могу предложить 2 варианта:
    1. Можно сделать любым объектом, хоть Сюзанной. Для этого нужно создать объекты единичного размера, сделать для каждого увеличение на длину ребра [sx,sy,sz], поворот по направлению вектора (грани) [rx,ry,rz] и переместить в нужную точку (вершину) [tx,ty,tz], по завершении применить булеву операцию Union. Но этот вариант далёк от совершенства.
    2. Сделать меш путём анализа смежных граней. Этот вариант будет лучшим.

    В любом случае читаем мат. часть по векторам, матрицам, кватернионам.
     
  3. dmtr

    dmtr Пользователь сайта

    С нами с:
    06.10.2008
    Сообщения:
    6
    Симпатии:
    0
    Баллы:
    2
    На счёт смежных граней не понял. Каким образом можно сделать объёмный каркас с помощью анализа смежных граней?
     
  4. logosman vip

    logosman Moderator Команда форума

    С нами с:
    24.09.2005
    Сообщения:
    196
    Симпатии:
    15
    Баллы:
    366
    Весь алгоритм описывать не буду, т.к. слишком долго, нет ни времени, ни желания. Только немного подтолкну.

    Пример привожу для 2D. Для 3D будет похоже.
    Допустим, есть вершина (o), которая соединяет 3 грани (-).

    Код:
      |
      o
    /    \
    
    Нам нужно сделать объём. Для этого нужно определить новые вершины между гранями, что я называю анализом.
    Вот эти вершины (x):

    Код:
        |
    x  o  x
     / x \
    
    Далее, по вершинам, сделать меш.

    Задача не из лёгких, но решаема.
    Если Вы не программист, браться не советую.

    P.S. Обход меша будет рекурсивный, советую делать деревьями или графами.
     
  5. dmtr

    dmtr Пользователь сайта

    С нами с:
    06.10.2008
    Сообщения:
    6
    Симпатии:
    0
    Баллы:
    2
    Пробовал оба варианта и в обоих в тупик зашёл.

    1. Для примера взял одно ребро, дальше можно по аналогии в цикле:
    Код:
    import bpy, Blender, math
    from math import *
    from Blender import *
    
    mesh  = Blender.Mesh.New()
    
    #создаём наш меш
    verts = [[1,0,0],[0,1,0]]
    mesh.verts.extend(verts)
    ed = [[0,1]]
    mesh.edges.extend(ed)
    
    x = mesh.verts[1].co.x - me.verts[0].co.x
    y = mesh.verts[1].co.y - me.verts[0].co.y
    z = mesh.verts[1].co.z - me.verts[0].co.z
    dl = sqrt(x**2+y**2+z**2) #вычисляем длину вектора
    cyl = Mesh.Primitives.Cylinder(8,0.1,dl)#делаем цилиндр нужной нам длины
    
    а вот дальше тупик. Если взять mesh.getEuler(), то углы Эйлера будут нулевые, так как, меш никуда не вращался. Да и к тому же, нужен угол наклона одного ребра, а не всего меша. Как узнать угол? Точнее тут не просто угол нужен, а кватернион или матрица вращения.

    2. Если следовать предложенному вами методу, то получится, что толщина новых рёбер будет разная, так как угол наклона рёбер разный может быть. А для того, что бы толщина рёбер была одинаковая, новые точки нужно создавать в плоскости перпендикулярной ребру. Опять же не понятно как это сделать. Через уравнение плоскости?
     
  6. logosman vip

    logosman Moderator Команда форума

    С нами с:
    24.09.2005
    Сообщения:
    196
    Симпатии:
    15
    Баллы:
    366
    1. Начало верное.

    "mesh.getEuler()" Вам не понадобится, т.к. нам не важно, вращался объект или нет, все действия должны производиться в локальной системе координат объекта, а не в мировой. Матрица поворота будет единичной.
    Поступить можно иначе. Нужно исходить из некоторой базисной системы координат. Представляем её в виде векторов:
    ox=(1,0,0)
    oy=(0,1,0)
    oz=(0,0,1)

    Это будет система меша "цилиндр".
    Вы вычислили вектор ребра, дальше просто по 3 плоскостям вычисляете угол между вектором "цилиндра" и проекцией на плоскости вектора грани.
    Вы получаете 3 угла, на которые дальше поворачиваете "цилиндр".

    2. Чтобы толщина рёбер не изменялась, необходимо задать дистанцию от вершины до новой точки радиусом сферы R.
    Положение точки можно определить через уравнение плоскости. Нормаль плоскости есть вектор грани.
     
  7. dmtr

    dmtr Пользователь сайта

    С нами с:
    06.10.2008
    Сообщения:
    6
    Симпатии:
    0
    Баллы:
    2
    Нашёл на http://blenderartists.org скрипт. Для моих целей подошёл.

    Содержимое файла mesh_cube_wire.py:
    Код:
    #!BPY
    """
    Name: 'Cube wireframe'
    Blender: 246
    Group: 'Mesh'
    Tooltip: 'Make a solid wireframe conecting cube nodes'
    """
    
    __author__ = "Alejandro Sierra"
    __url__ = ("www.atzibala.com/blender/cube_wire/")
    __version__ = "1.0"
    
    __bpydoc__ = """\
    This script makes a solid object from selected edges. It's like Ideasman's
    solid wire but simpler: cube nodes are created in all selected vertex, then 
    connected by extruding the corresponging faces.
    """
    
    # --------------------------------------------------------------------------
    # Copyright (C) 2008 Alejandro Sierra (AKA naturalpainter or atzibala)
    # --------------------------------------------------------------------------
    # ***** 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 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
    #
    # ***** END GPL LICENCE BLOCK *****
    # --------------------------------------------------------------------------
    
    
    from Blender import Scene, Mesh, Window, sys, Draw
    from Blender.Mathutils import *
    import BPyMessages
    import bpy
    
    
    def create_cube(me, v, d):
    	x = v.co.x
    	y = v.co.y
    	z = v.co.z
    	coords=[ [x-d,y-d,z-d], [x+d,y-d,z-d], [x+d,y+d,z-d], [x-d,y+d,z-d],
    		 [x-d,y-d,z+d], [x+d,y-d,z+d], [x+d,y+d,z+d], [x-d,y+d,z+d] ]
    	me.verts.extend(coords)
    
    
    def find_intersected_face(v, face_used=-1):
    	normals=[ [0,0,1], [0,0,-1], [0,1,0], [0,-1,0], [-1,0,0], [1,0,0] ]
    	
    	dotmax = 0
    	index = 0
    	vn = v.normalize()
    	for i in range(6):
    		n = normals[i]
    		a = Vector(n[0], n[1], n[2])
    		d = -a*vn
    		if d > dotmax and face_used!=i:
    			dotmax=d
    			index = i
    
    	return index, dotmax
    
    
    def fill_cube_face(me, index, f):
    	faces= [ [3,2,1,0], [4,5,6,7], [0,1,5,4],
    		 [7,6,2,3], [1,2,6,5], [4,7,3,0] ]
    
    	me.faces.extend([ index+faces[f][0], 
    			  index+faces[f][1],
    			  index+faces[f][2],
    			  index+faces[f][3]])
    
    def skin_edges(me, i1, i2, f1, f2):
    	faces= [ [3,2,1,0], [4,5,6,7], [0,1,5,4],
    		 [7,6,2,3], [1,2,6,5], [4,7,3,0] ]
    
    	me.faces.extend([ i1+faces[f1][0], 
    			  i2+faces[f2][3],
    			  i2+faces[f2][2],
    			  i1+faces[f1][1]])
    
    	me.faces.extend([ i1+faces[f1][1], 
    			  i2+faces[f2][2],
    			  i2+faces[f2][1],
    			  i1+faces[f1][2]])
    
    	me.faces.extend([ i1+faces[f1][2], 
    			  i2+faces[f2][1],
    			  i2+faces[f2][0],
    			  i1+faces[f1][3]])
    
    	me.faces.extend([ i1+faces[f1][3], 
    			  i2+faces[f2][0],
    			  i2+faces[f2][3],
    			  i1+faces[f1][0]])
    
    
    def create_wired_mesh(me, thick):
    	node = {}
    
    	# Create node list
    	for e in me.edges:
    		if e.sel:
    			i0 = e.key[0]
    			i1 = e.key[1]
    			if not node.has_key(i0):
    				node[i0] = []
    			if not node.has_key(i1):
    				node[i1] = []
    			f1, d = find_intersected_face(me.verts[i1].co-me.verts[i0].co)
    	
    			if (f1 % 2 == 0):
    				f2 = f1 + 1
    			else:
    				f2 = f1 - 1
    
    			# adjust faces in case they are repeated
    			for i in range(len(node[i0])):
    				if node[i0][i][1] == f1:
    					d2 = node[i0][i][3]
    					if d < d2:						
    						f1, d = find_intersected_face(me.verts[i1].co-me.verts[i0].co, f1)
    					else:
    						i2 = node[i0][i][0]
    						f3, d3= find_intersected_face(me.verts[i2].co-me.verts[i0].co, f1)
    						node[i0][i][2] = f3
    						node[i0][i][3] = d3
    
    			for i in range(len(node[i1])):
    				if node[i1][i][1] == f2:
    					d2 = node[i1][i][3]
    					if d < d2:						
    						f2, d = find_intersected_face(me.verts[i0].co-me.verts[i1].co, f2)
    					else:
    						i2 = node[i1][i][0]
    						f3, d3= find_intersected_face(me.verts[i2].co-me.verts[i1].co, f2)
    						node[i1][i][2] = f3
    						node[i1][i][3] = d3
    				
    			node[i0].append([i1, f1, f2, d])
    			node[i1].append([i0, f2, f1, d])	
    
    	me2 = bpy.data.meshes.new(me.name)
    
    	# Create the geometry
    	n_idx = {}   
    	for k in node:
    		v = me.verts[k]
    		index = len(me2.verts)
    		# We need to associate each node with the new geometry
    		n_idx[k] = index   
    		# Geometry for the nodes, each one a cube
    		create_cube(me2, v, thick)
    		
    		
    	# Skin using the new geometry	
    	for k in node:
    		# Compute the face list
    		f = [-1,-1,-1,-1,-1,-1] 
    		n = node[k]
    		
    		for i in range(len(n)):
    			f1 = n[i][1]
    			f[f1] = i
    
    
    		# Skin the nodes				
    		for i in range(6):
    			if f[i] == -1:
    				fill_cube_face(me2, n_idx[k], i)	
    			else:
    				k2 = n[f[i]][0]
    				f1 = n[f[i]][1]
    				f2 = n[f[i]][2]
    				skin_edges(me2, n_idx[k], n_idx[k2], f1, f2)
    
    	print 'vert count', len(me2.verts)
            print 'edge count', len(me2.edges)
            print 'face count', len(me2.faces)
    
    	scn = bpy.data.scenes.active
    	ob = scn.objects.new(me2, me2.name)
    
    
    
    def main():
    	THICK = Draw.Create(0.02)
    
    	if not Draw.PupBlock('Cube wireframe', [\
            ('Thick:', THICK, 0.0001, 10, 'Thickness of the skinned edges'),\
    
            ]):
                    return
    
    	# Gets the current scene, there can be many scenes in 1 blend file.
    	sce = bpy.data.scenes.active
    	
    	# Get the active object, there can only ever be 1
    	# and the active object is always the editmode object.
    	ob_act = sce.objects.active
    	
    	if not ob_act or ob_act.type != 'Mesh':
    		BPyMessages.Error_NoMeshActive()
    		return 
    	
    	
    	# Saves the editmode state and go's out of 
    	# editmode if its enabled, we cant make
    	# changes to the mesh data while in editmode.
    	is_editmode = Window.EditMode()
    	if is_editmode: Window.EditMode(0)
    	
    	Window.WaitCursor(1)
    	me = ob_act.getData(mesh=1) # old NMesh api is default
    	t = sys.time()
    	
    	# Run the mesh editing function
    	create_wired_mesh(me, THICK.val/2.0)
    
    	ob_act.select(False)
    	
    	# Restore editmode if it was enabled
    	if is_editmode: Window.EditMode(1)
    
    	# Timing the script is a good way to be aware on any speed hits when scripting
    	print 'My Script finished in %.2f seconds' % (sys.time()-t)
    	Window.WaitCursor(0)
    	
    	
    # This lets you import the script without running it
    if __name__ == '__main__':
    	main()
    
     
  8. logosman vip

    logosman Moderator Команда форума

    С нами с:
    24.09.2005
    Сообщения:
    196
    Симпатии:
    15
    Баллы:
    366
    Как вариант, но результат корявый.
     
Модераторы: logosman

Поделиться этой страницей