Welcome to mirror list, hosted at ThFree Co, Russian Federation.

mesh_wire.py « scripts « release - git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: bd38c47a9b923f779edaf38a4e30c451f374be07 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
#!BPY
"""
Name: 'Solid Wireframe'
Blender: 243
Group: 'Mesh'
Tooltip: 'Make a solid wireframe copy of this mesh'
"""

# -------------------------------------------------------------------------- 
# Solid Wireframe1.0 by Campbell Barton (AKA Ideasman42) 
# -------------------------------------------------------------------------- 
# ***** 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 ***** 
# --------------------------------------------------------------------------
import Blender
from Blender import Scene, Mesh, Window, sys
from Blender.Mathutils import AngleBetweenVecs, TriangleNormal
from BPyMesh import faceAngles # get angles for face cornders
#import BPyMesh
#reload(BPyMesh)
#faceAngles = BPyMesh.faceAngles

# works out the distanbce to inset the corners based on angles
from BPyMathutils import angleToLength
#import BPyMathutils 
#reload(BPyMathutils)
#angleToLength = BPyMathutils.angleToLength

import mesh_solidify

import BPyMessages
reload(BPyMessages)
import bpy


def solid_wire(ob_orig, me_orig, sce, PREF_THICKNESS, PREF_SOLID, PREF_SHARP, PREF_XSHARP):
	if not PREF_SHARP and PREF_XSHARP:
		PREF_XSHARP = False
	
	# This function runs out of editmode with a mesh
	# error cases are alredy checked for
	
	inset_half = PREF_THICKNESS / 2
	del PREF_THICKNESS
	
	ob = ob_orig.copy()
	me = me_orig.copy()
	ob.link(me)
	sce.objects.selected = []
	sce.objects.link(ob)
	ob.sel = True
	sce.objects.active = ob
	
	# Modify the object, should be a set
	FGON= Mesh.EdgeFlags.FGON
	edges_fgon = dict([(ed.key,None) for ed in me.edges if ed.flag & FGON])
	# edges_fgon.fromkeys([ed.key for ed in me.edges if ed.flag & FGON])
	
	del FGON

	
	
	# each face needs its own verts
	# orig_vert_count =len(me.verts)
	new_vert_count = len(me.faces) * 4
	for f in me.faces:
		if len(f) == 3:
			new_vert_count -= 1
	
	if PREF_SHARP == 0:
		new_faces_edge= {}
		
		def add_edge(i1,i2, ni1, ni2):
			
			if i1>i2:
				i1,i2 = i2,i1
				flip = True
			else:
				flip = False
			new_faces_edge.setdefault((i1,i2), []).append((ni1, ni2, flip))
		
	
	new_verts = []
	new_faces = []
	vert_index = len(me.verts)
	
	for f in me.faces:
		f_v_co = [v.co for v in f]
		angles = faceAngles(f_v_co)
		f_v_idx = [v.index for v in f]
		
		def new_vert(fi):
			co = f_v_co[fi]
			a = angles[fi]
			if a > 180:
				vert_inset = 1 * inset_half
			else:
				vert_inset = inset_half * angleToLength( abs((180-a) / 2) )
			
			# Calculate the inset direction
			co1 = f_v_co[fi-1]
			co2 = fi+1 # Wrap this index back to the start
			if co2 == len(f_v_co): co2 = 0
			co2 = f_v_co[co2]
			
			co1 = co1 - co
			co2 = co2 - co
			co1.normalize()
			co2.normalize()
			d = co1+co2
			# Done with inset direction
			
			d.length = vert_inset
			return co+d
		
		new_verts.extend([new_vert(i) for i in xrange(len(f_v_co))])
		
		if len(f_v_idx) == 4:
			faces = [\
			(f_v_idx[1], f_v_idx[0], vert_index, vert_index+1),\
			(f_v_idx[2], f_v_idx[1], vert_index+1, vert_index+2),\
			(f_v_idx[3], f_v_idx[2], vert_index+2, vert_index+3),\
			(f_v_idx[0], f_v_idx[3], vert_index+3, vert_index),\
			]
		else:
			faces = [\
			(f_v_idx[1], f_v_idx[0], vert_index, vert_index+1),\
			(f_v_idx[2], f_v_idx[1], vert_index+1, vert_index+2),\
			(f_v_idx[0], f_v_idx[2], vert_index+2, vert_index),\
			]
		
		
		if PREF_SHARP == 1:
			if not edges_fgon:
				new_faces.extend(faces)
			else:
				for nf in faces:
					i1,i2 = nf[0], nf[1]
					if i1>i2: i1,i2 = i2,i1
					
					if edges_fgon and (i1,i2) not in edges_fgon:
						new_faces.append(nf)
			
			
		
		elif PREF_SHARP == 0:
			for nf in faces:
				add_edge(*nf)
			
		vert_index += len(f_v_co)
	
	me.verts.extend(new_verts)
	
	if PREF_SHARP == 0:
		def add_tri_flipped(i1,i2,i3):
			try:
				if AngleBetweenVecs(me.verts[i1].no, TriangleNormal(me.verts[i1].co, me.verts[i2].co, me.verts[i3].co)) < 90:
					return i3,i2,i1
				else:
					return i1,i2,i3
			except:
				return i1,i2,i3
		
		# This stores new verts that use this vert
		# used for re-averaging this verts location
		# based on surrounding verts. looks better but not needed.
		vert_users = [set() for i in xrange(vert_index)]
		
		for (i1,i2), nf in new_faces_edge.iteritems():
			
			if len(nf) == 2:
				# Add the main face
				if edges_fgon and (i1,i2) not in edges_fgon:
					new_faces.append((nf[0][0], nf[0][1], nf[1][0], nf[1][1]))
				
				
				if nf[0][2]:	key1 = nf[0][1],nf[0][0]
				else:			key1 = nf[0][0],nf[0][1]
				if nf[1][2]:	key2 = nf[1][1],nf[1][0]
				else:			key2 = nf[1][0],nf[1][1]
				
				# CRAP, cont work out which way to flip so make it oppisite the verts normal.
				
				###new_faces.append((i2, key1[0], key2[0])) # NO FLIPPING, WORKS THOUGH
				###new_faces.append((i1, key1[1], key2[1]))
				new_faces.append(add_tri_flipped(i2, key1[0], key2[0]))
				new_faces.append(add_tri_flipped(i1, key1[1], key2[1]))
				
				# Average vert loction so its not tooo pointy
				# not realy needed but looks better
				vert_users[i2].update((key1[0], key2[0]))
				vert_users[i1].update((key1[1], key2[1]))
			
			if len(nf) == 1:
				if nf[0][2]:	new_faces.append((nf[0][0], nf[0][1], i2, i1)) # flipped
				else:			new_faces.append((i1,i2, nf[0][0], nf[0][1]))
				
		
		# average points now.
		for i, vusers in enumerate(vert_users):
			if vusers:
				co = me.verts[i].co
				co.zero()
				
				for ii in vusers:
					co += me.verts[ii].co
				co /= len(vusers)
	
	me.faces.delete(1, range(len(me.faces)))
	
	me.faces.extend(new_faces)

	# External function, solidify
	me.sel = True
	if PREF_SOLID:
		mesh_solidify.solidify(me, -inset_half*2, True, False, PREF_XSHARP)


def main():
	
	# 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()
	Window.EditMode(0)
	
	me = ob_act.getData(mesh=1) # old NMesh api is default
	if len(me.faces)==0:
		BPyMessages.Error_NoMeshFaces()
		if is_editmode: Window.EditMode(1)
		return
	
	# Create the variables.
	PREF_THICK = Blender.Draw.Create(0.005)
	PREF_SOLID = Blender.Draw.Create(1)
	PREF_SHARP = Blender.Draw.Create(1)
	PREF_XSHARP = Blender.Draw.Create(0)
	
	pup_block = [\
	('Thick:', PREF_THICK, 0.0001, 2.0, 'Skin thickness in mesh space.'),\
	('Solid Wire', PREF_SOLID, 'If Disabled, will use 6 sided wire segments'),\
	('Sharp Wire', PREF_SHARP, 'Use the original mesh topology for more accurate sharp wire.'),\
	('Extra Sharp', PREF_XSHARP, 'Use less geometry to create a sharper looking wire'),\
	]
	
	if not Blender.Draw.PupBlock('Solid Wireframe', pup_block):
		if is_editmode: Window.EditMode(1)
		return
	
	Window.WaitCursor(1)
	t = sys.time()
	
	# Run the mesh editing function
	solid_wire(ob_act, me, sce, PREF_THICK.val, PREF_SOLID.val, PREF_SHARP.val, PREF_XSHARP.val)
	
	# Timing the script is a good way to be aware on any speed hits when scripting
	print 'Solid Wireframe finished in %.2f seconds' % (sys.time()-t)
	Window.WaitCursor(0)
	if is_editmode: Window.EditMode(1)
	
	
# This lets you can import the script without running it
if __name__ == '__main__':
	main()