forked from bartvdbraak/blender
- grease pencil drawing on the surface of objects (only when enable face snap & projection, a bit hidden I know)
- retopo operator to convert grease pencil drawn topology into geometry, not in the convert menu yet since its not quite finished, use the operator search menu for retopo. will test this week and see what needs fixing.
This commit is contained in:
parent
3d1f297489
commit
dc1af66d8a
291
release/scripts/modules/retopo.py
Normal file
291
release/scripts/modules/retopo.py
Normal file
@ -0,0 +1,291 @@
|
||||
# ##### 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 LICENSE BLOCK #####
|
||||
|
||||
# <pep8 compliant>
|
||||
|
||||
import bpy
|
||||
|
||||
EPS = 0.001
|
||||
EPS_LINE_LINE = 0.02
|
||||
EPS_COLLAPSE = 0.05
|
||||
EPS_HUB = 0.05
|
||||
|
||||
def get_hub(co, _hubs):
|
||||
|
||||
if 1:
|
||||
for hub in _hubs.values():
|
||||
if (hub.co - co).length < EPS_HUB:
|
||||
return hub
|
||||
|
||||
key = co.toTuple(3)
|
||||
hub = _hubs[key] = Hub(co, key, len(_hubs))
|
||||
return hub
|
||||
else:
|
||||
pass
|
||||
|
||||
'''
|
||||
key = co.toTuple(3)
|
||||
try:
|
||||
return _hubs[key]
|
||||
except:
|
||||
hub = _hubs[key] = Hub(co, key, len(_hubs))
|
||||
return hub
|
||||
'''
|
||||
|
||||
|
||||
class Hub:
|
||||
def __init__(self, co, key, index):
|
||||
self.co = co.copy()
|
||||
self.key = key
|
||||
self.index = index
|
||||
self.links = []
|
||||
|
||||
def get_weight(self):
|
||||
f = 0.0
|
||||
|
||||
for hub_other in self.links:
|
||||
f += (self.co - hub_other.co).length
|
||||
|
||||
def replace(self, other):
|
||||
for hub in self.links:
|
||||
try:
|
||||
hub.links.remove(self)
|
||||
except:
|
||||
pass
|
||||
if other not in hub.links:
|
||||
hub.links.append(other)
|
||||
|
||||
|
||||
def dist(self, other):
|
||||
return (self.co - other.co).length
|
||||
|
||||
def calc_faces(self, hub_ls):
|
||||
faces = []
|
||||
# first tris
|
||||
for l_a in self.links:
|
||||
for l_b in l_a.links:
|
||||
if l_b is not self and l_b in self.links:
|
||||
# will give duplicates
|
||||
faces.append((self.index, l_a.index, l_b.index))
|
||||
|
||||
# now quads, check which links share 2 different verts
|
||||
# directly
|
||||
def validate_quad(face):
|
||||
if len(set(face)) != len(face):
|
||||
return False
|
||||
if hub_ls[face[0]] in hub_ls[face[2]].links:
|
||||
return False
|
||||
if hub_ls[face[2]] in hub_ls[face[0]].links:
|
||||
return False
|
||||
|
||||
if hub_ls[face[1]] in hub_ls[face[3]].links:
|
||||
return False
|
||||
if hub_ls[face[3]] in hub_ls[face[1]].links:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
for i, l_a in enumerate(self.links):
|
||||
links_a = set([l.index for l in l_a.links])
|
||||
for j in range(i):
|
||||
l_b = self.links[j]
|
||||
|
||||
links_b = set([l.index for l in l_b.links])
|
||||
|
||||
isect = links_a.intersection(links_b)
|
||||
if len(isect) == 2:
|
||||
isect = list(isect)
|
||||
|
||||
# check there are no diagonal lines
|
||||
face = (isect[0], l_a.index, isect[1], l_b.index)
|
||||
if validate_quad(face):
|
||||
|
||||
faces.append(face)
|
||||
|
||||
return faces
|
||||
|
||||
|
||||
|
||||
class Spline:
|
||||
def __init__(self, points):
|
||||
self.points = points
|
||||
self.hubs = []
|
||||
|
||||
def link(self):
|
||||
if len(self.hubs) < 2:
|
||||
return
|
||||
|
||||
edges = list(set([i for i, hub in self.hubs]))
|
||||
edges.sort()
|
||||
|
||||
edges_order = {}
|
||||
for i in edges:
|
||||
edges_order[i] = []
|
||||
|
||||
|
||||
# self.hubs.sort()
|
||||
for i, hub in self.hubs:
|
||||
edges_order[i].append(hub)
|
||||
|
||||
hubs_order = []
|
||||
for i in edges:
|
||||
ls = edges_order[i]
|
||||
edge_start = self.points[i]
|
||||
ls.sort(key=lambda hub: (hub.co - edge_start).length)
|
||||
hubs_order.extend(ls)
|
||||
|
||||
# Now we have the order, connect the hubs
|
||||
hub_prev = hubs_order[0]
|
||||
|
||||
for hub in hubs_order[1:]:
|
||||
hub.links.append(hub_prev)
|
||||
hub_prev.links.append(hub)
|
||||
hub_prev = hub
|
||||
|
||||
|
||||
|
||||
def get_points(spline):
|
||||
points = spline.points
|
||||
|
||||
if len(spline.bezier_points):
|
||||
points = spline.bezier_points
|
||||
|
||||
return [point.co.copy().resize3D() for point in points]
|
||||
|
||||
|
||||
def get_splines(data):
|
||||
return [Spline(get_points(spline)) for spline in data.splines]
|
||||
|
||||
def xsect_spline(sp_a, sp_b, _hubs):
|
||||
from Mathutils import LineIntersect
|
||||
from Mathutils import MidpointVecs
|
||||
from Geometry import ClosestPointOnLine
|
||||
pt_a_prev = pt_b_prev = None
|
||||
|
||||
pt_a_prev = sp_a.points[0]
|
||||
for a, pt_a in enumerate(sp_a.points[1:]):
|
||||
pt_b_prev = sp_b.points[0]
|
||||
for b, pt_b in enumerate(sp_b.points[1:]):
|
||||
|
||||
# Now we have 2 edges
|
||||
# print(pt_a, pt_a_prev, pt_b, pt_b_prev)
|
||||
xsect = LineIntersect(pt_a, pt_a_prev, pt_b, pt_b_prev)
|
||||
if xsect is not None:
|
||||
if (xsect[0]-xsect[1]).length <= EPS_LINE_LINE:
|
||||
f = ClosestPointOnLine(xsect[1], pt_a, pt_a_prev)[1]
|
||||
if f >= 0.0 and f <= 1.0:
|
||||
f = ClosestPointOnLine(xsect[0], pt_b, pt_b_prev)[1]
|
||||
if f >= 0.0 and f <= 1.0:
|
||||
# This wont happen often
|
||||
co = MidpointVecs(xsect[0], xsect[1])
|
||||
hub = get_hub(co, _hubs)
|
||||
|
||||
sp_a.hubs.append((a, hub))
|
||||
sp_b.hubs.append((b, hub))
|
||||
|
||||
pt_b_prev = pt_b
|
||||
|
||||
pt_a_prev = pt_a
|
||||
|
||||
|
||||
def calculate(scene, obj):
|
||||
data = obj.data
|
||||
splines = get_splines(data)
|
||||
_hubs = {}
|
||||
|
||||
for i, sp in enumerate(splines):
|
||||
for j, sp_other in enumerate(splines):
|
||||
if j<=i:
|
||||
continue
|
||||
|
||||
xsect_spline(sp, sp_other, _hubs)
|
||||
|
||||
for sp in splines:
|
||||
sp.link()
|
||||
|
||||
# remove these
|
||||
hubs_ls = [hub for hub in _hubs.values() if hub.index != -1]
|
||||
|
||||
_hubs.clear()
|
||||
_hubs = None
|
||||
|
||||
for i, hub in enumerate(hubs_ls):
|
||||
hub.index = i
|
||||
|
||||
# Now we have connected hubs, write all edges!
|
||||
def order(i1, i2):
|
||||
if i1 > i2:
|
||||
return i2, i1
|
||||
return i1, i2
|
||||
|
||||
edges = {}
|
||||
|
||||
for hub in hubs_ls:
|
||||
i1 = hub.index
|
||||
for hub_other in hub.links:
|
||||
i2 = hub_other.index
|
||||
edges[order(i1, i2)] = None
|
||||
|
||||
verts = []
|
||||
edges = edges.keys()
|
||||
faces = []
|
||||
|
||||
for hub in hubs_ls:
|
||||
verts.append(hub.co)
|
||||
faces.extend(hub.calc_faces(hubs_ls))
|
||||
|
||||
# remove double faces
|
||||
faces = dict([(tuple(sorted(f)), f) for f in faces]).values()
|
||||
|
||||
mesh = bpy.data.add_mesh("Retopo")
|
||||
mesh.from_pydata(verts, [], faces)
|
||||
|
||||
scene = bpy.context.scene
|
||||
mesh.update()
|
||||
obj_new = bpy.data.add_object('MESH', "Torus")
|
||||
obj_new.data = mesh
|
||||
scene.objects.link(obj_new)
|
||||
|
||||
return obj_new
|
||||
|
||||
|
||||
def main():
|
||||
# first convert gpencil
|
||||
# *** evil!
|
||||
scene = bpy.context.scene
|
||||
|
||||
bpy.ops.gpencil.convert(type='PATH')
|
||||
|
||||
|
||||
scene = bpy.context.scene
|
||||
obj = bpy.context.object
|
||||
if not obj:
|
||||
raise Exception("no active object")
|
||||
|
||||
obj_new = calculate(scene, obj)
|
||||
|
||||
# obj.selected = False
|
||||
scene.objects.unlink(obj)
|
||||
|
||||
scene.objects.active = obj_new
|
||||
obj_new.selected = True
|
||||
|
||||
# nasty, recalc normals
|
||||
bpy.ops.object.mode_set(mode='EDIT', toggle=False)
|
||||
bpy.ops.mesh.normals_make_consistent(inside=False)
|
||||
bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
|
@ -95,5 +95,20 @@ class SubsurfSet(bpy.types.Operator):
|
||||
return ('FINISHED',)
|
||||
|
||||
|
||||
class Retopo(bpy.types.Operator):
|
||||
'''TODO - doc'''
|
||||
|
||||
bl_idname = "object.retopology"
|
||||
bl_label = "Retopology from Grease Pencil"
|
||||
bl_register = True
|
||||
bl_undo = True
|
||||
|
||||
def execute(self, context):
|
||||
import retopo
|
||||
retopo.main()
|
||||
return ('FINISHED',)
|
||||
|
||||
|
||||
bpy.ops.add(SelectPattern)
|
||||
bpy.ops.add(SubsurfSet)
|
||||
bpy.ops.add(Retopo)
|
||||
|
@ -531,7 +531,7 @@ static void gp_layer_to_curve (bContext *C, bGPdata *gpd, bGPDlayer *gpl, short
|
||||
}
|
||||
|
||||
/* restore old active object */
|
||||
BASACT= base;
|
||||
// BASACT= base; // removing since this is expected new objects are active.
|
||||
}
|
||||
|
||||
/* --- */
|
||||
|
@ -151,6 +151,21 @@ static int gpencil_draw_poll (bContext *C)
|
||||
return (gpencil_data_get_pointers(C, NULL) != NULL);
|
||||
}
|
||||
|
||||
static int gpencil_project_check(tGPsdata *p)
|
||||
{
|
||||
bGPdata *gpd= p->gpd;
|
||||
|
||||
/* in 3d-space - pt->x/y/z are 3 side-by-side floats */
|
||||
if( (gpd->sbuffer_sflag & GP_STROKE_3DSPACE) &&
|
||||
(p->scene->toolsettings->snap_mode==SCE_SNAP_MODE_FACE) &&
|
||||
(p->scene->toolsettings->snap_flag & SCE_SNAP_PROJECT) )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ******************************************* */
|
||||
/* Calculations/Conversions */
|
||||
|
||||
@ -211,24 +226,29 @@ static void gp_stroke_convertcoords (tGPsdata *p, short mval[], float out[])
|
||||
|
||||
/* in 3d-space - pt->x/y/z are 3 side-by-side floats */
|
||||
if (gpd->sbuffer_sflag & GP_STROKE_3DSPACE) {
|
||||
const short mx=mval[0], my=mval[1];
|
||||
float rvec[3], dvec[3];
|
||||
|
||||
/* Current method just converts each point in screen-coordinates to
|
||||
* 3D-coordinates using the 3D-cursor as reference. In general, this
|
||||
* works OK, but it could of course be improved.
|
||||
*
|
||||
* TODO:
|
||||
* - investigate using nearest point(s) on a previous stroke as
|
||||
* reference point instead or as offset, for easier stroke matching
|
||||
* - investigate projection onto geometry (ala retopo)
|
||||
*/
|
||||
gp_get_3d_reference(p, rvec);
|
||||
|
||||
/* method taken from editview.c - mouse_cursor() */
|
||||
project_short_noclip(p->ar, rvec, mval);
|
||||
window_to_3d_delta(p->ar, dvec, mval[0]-mx, mval[1]-my);
|
||||
sub_v3_v3v3(out, rvec, dvec);
|
||||
if(gpencil_project_check(p) && (view_autodist_simple(p->ar, mval, out))) {
|
||||
/* pass */
|
||||
}
|
||||
else {
|
||||
const short mx=mval[0], my=mval[1];
|
||||
float rvec[3], dvec[3];
|
||||
|
||||
/* Current method just converts each point in screen-coordinates to
|
||||
* 3D-coordinates using the 3D-cursor as reference. In general, this
|
||||
* works OK, but it could of course be improved.
|
||||
*
|
||||
* TODO:
|
||||
* - investigate using nearest point(s) on a previous stroke as
|
||||
* reference point instead or as offset, for easier stroke matching
|
||||
*/
|
||||
|
||||
gp_get_3d_reference(p, rvec);
|
||||
|
||||
/* method taken from editview.c - mouse_cursor() */
|
||||
project_short_noclip(p->ar, rvec, mval);
|
||||
window_to_3d_delta(p->ar, dvec, mval[0]-mx, mval[1]-my);
|
||||
sub_v3_v3v3(out, rvec, dvec);
|
||||
}
|
||||
}
|
||||
|
||||
/* 2d - on 'canvas' (assume that p->v2d is set) */
|
||||
@ -1114,6 +1134,12 @@ static void gpencil_draw_exit (bContext *C, wmOperator *op)
|
||||
}
|
||||
|
||||
/* cleanup */
|
||||
if(gpencil_project_check(p)) {
|
||||
View3D *v3d= p->sa->spacedata.first;
|
||||
view3d_operator_needs_opengl(C);
|
||||
view_autodist_init(p->scene, p->ar, v3d);
|
||||
}
|
||||
|
||||
gp_paint_cleanup(p);
|
||||
gp_session_cleanup(p);
|
||||
|
||||
|
@ -117,8 +117,13 @@ unsigned int view3d_sample_backbuf_rect(struct ViewContext *vc, short mval[2], i
|
||||
void *handle, unsigned int (*indextest)(void *handle, unsigned int index));
|
||||
unsigned int view3d_sample_backbuf(struct ViewContext *vc, int x, int y);
|
||||
|
||||
/* draws and does a 4x4 sample */
|
||||
int view_autodist(struct Scene *scene, struct ARegion *ar, struct View3D *v3d, short *mval, float mouse_worldloc[3]);
|
||||
|
||||
/* only draw so view_autodist_simple can be called many times after */
|
||||
int view_autodist_init(struct Scene *scene, struct ARegion *ar, struct View3D *v3d);
|
||||
int view_autodist_simple(struct ARegion *ar, short *mval, float mouse_worldloc[3]);
|
||||
|
||||
/* select */
|
||||
#define MAXPICKBUF 10000
|
||||
short view3d_opengl_select(struct ViewContext *vc, unsigned int *buffer, unsigned int bufsize, rcti *input);
|
||||
|
@ -2205,7 +2205,53 @@ int view_autodist(Scene *scene, ARegion *ar, View3D *v3d, short *mval, float mou
|
||||
return 1;
|
||||
}
|
||||
|
||||
int view_autodist_init(Scene *scene, ARegion *ar, View3D *v3d) //, float *autodist )
|
||||
{
|
||||
RegionView3D *rv3d= ar->regiondata;
|
||||
|
||||
/* Get Z Depths, needed for perspective, nice for ortho */
|
||||
draw_depth(scene, ar, v3d, NULL);
|
||||
|
||||
/* force updating */
|
||||
if (rv3d->depths) {
|
||||
rv3d->depths->damaged = 1;
|
||||
}
|
||||
|
||||
view3d_update_depths(ar, v3d);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// no 4x4 sampling, run view_autodist_init first
|
||||
int view_autodist_simple(ARegion *ar, short *mval, float mouse_worldloc[3] ) //, float *autodist )
|
||||
{
|
||||
RegionView3D *rv3d= ar->regiondata;
|
||||
bglMats mats; /* ZBuffer depth vars, could cache? */
|
||||
float depth;
|
||||
double cent[2], p[3];
|
||||
|
||||
if (mval[0] < 0) return 0;
|
||||
if (mval[1] < 0) return 0;
|
||||
if (mval[0] >= rv3d->depths->w) return 0;
|
||||
if (mval[1] >= rv3d->depths->h) return 0;
|
||||
|
||||
/* Get Z Depths, needed for perspective, nice for ortho */
|
||||
bgl_get_mats(&mats);
|
||||
depth= rv3d->depths->depths[mval[1]*rv3d->depths->w+mval[0]];
|
||||
|
||||
if (depth==MAXFLOAT)
|
||||
return 0;
|
||||
|
||||
cent[0] = (double)mval[0];
|
||||
cent[1] = (double)mval[1];
|
||||
|
||||
if (!gluUnProject(cent[0], cent[1], depth, mats.modelview, mats.projection, (GLint *)mats.viewport, &p[0], &p[1], &p[2]))
|
||||
return 0;
|
||||
|
||||
mouse_worldloc[0] = (float)p[0];
|
||||
mouse_worldloc[1] = (float)p[1];
|
||||
mouse_worldloc[2] = (float)p[2];
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ********************* NDOF ************************ */
|
||||
/* note: this code is confusing and unclear... (ton) */
|
||||
|
Loading…
Reference in New Issue
Block a user