Brought back sculpt code. Commented out a lot of things, so it compiles.
Does nothing more impressive than that for now.
This commit is contained in:
2137
source/blender/editors/sculpt/sculpt.c
Normal file
2137
source/blender/editors/sculpt/sculpt.c
Normal file
@@ -0,0 +1,2137 @@
|
||||
/*
|
||||
* $Id: sculptmode.c 18309 2009-01-04 07:47:11Z nicholasbishop $
|
||||
*
|
||||
* ***** 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.
|
||||
*
|
||||
* The Original Code is Copyright (C) 2006 by Nicholas Bishop
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): none yet.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*
|
||||
* Implements the Sculpt Mode tools
|
||||
*
|
||||
* BDR_sculptmode.h
|
||||
*
|
||||
*/
|
||||
|
||||
#include "GHOST_Types.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_arithb.h"
|
||||
#include "BLI_blenlib.h"
|
||||
#include "BLI_dynstr.h"
|
||||
|
||||
#include "DNA_armature_types.h"
|
||||
#include "DNA_brush_types.h"
|
||||
#include "DNA_image_types.h"
|
||||
#include "DNA_key_types.h"
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_meshdata_types.h"
|
||||
#include "DNA_modifier_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_screen_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_texture_types.h"
|
||||
#include "DNA_view3d_types.h"
|
||||
#include "DNA_userdef_types.h"
|
||||
#include "DNA_color_types.h"
|
||||
|
||||
#include "BKE_customdata.h"
|
||||
#include "BKE_DerivedMesh.h"
|
||||
#include "BKE_depsgraph.h"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_image.h"
|
||||
#include "BKE_key.h"
|
||||
#include "BKE_library.h"
|
||||
#include "BKE_main.h"
|
||||
#include "BKE_mesh.h"
|
||||
#include "BKE_modifier.h"
|
||||
#include "BKE_multires.h"
|
||||
#include "BKE_sculpt.h"
|
||||
#include "BKE_texture.h"
|
||||
#include "BKE_utildefines.h"
|
||||
#include "BKE_colortools.h"
|
||||
|
||||
#include "BIF_gl.h"
|
||||
#include "BIF_glutil.h"
|
||||
|
||||
#include "sculpt_intern.h"
|
||||
|
||||
#include "IMB_imbuf_types.h"
|
||||
|
||||
#include "RE_render_ext.h"
|
||||
#include "RE_shader_ext.h" /*for multitex_ext*/
|
||||
|
||||
#include "GPU_draw.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Number of vertices to average in order to determine the flatten distance */
|
||||
#define FLATTEN_SAMPLE_SIZE 10
|
||||
|
||||
/* Texture cache size */
|
||||
#define TC_SIZE 256
|
||||
|
||||
/* ===== STRUCTS =====
|
||||
*
|
||||
*/
|
||||
|
||||
/* ActiveData stores an Index into the mvert array of Mesh, plus Fade, which
|
||||
stores how far the vertex is from the brush center, scaled to the range [0,1]. */
|
||||
typedef struct ActiveData {
|
||||
struct ActiveData *next, *prev;
|
||||
unsigned int Index;
|
||||
float Fade;
|
||||
float dist;
|
||||
} ActiveData;
|
||||
|
||||
typedef struct BrushActionSymm {
|
||||
float center_3d[3];
|
||||
char index;
|
||||
|
||||
float up[3], right[3], out[3];
|
||||
|
||||
/* Grab brush */
|
||||
float grab_delta[3];
|
||||
} BrushActionSymm;
|
||||
|
||||
typedef struct BrushAction {
|
||||
BrushActionSymm symm;
|
||||
|
||||
char firsttime;
|
||||
|
||||
/* Some brushes need access to original mesh vertices */
|
||||
vec3f *mesh_store;
|
||||
short (*orig_norms)[3];
|
||||
|
||||
short mouse[2];
|
||||
float size_3d;
|
||||
|
||||
float prev_radius;
|
||||
float radius;
|
||||
|
||||
float *layer_disps;
|
||||
char flip;
|
||||
|
||||
char clip[3];
|
||||
float cliptol[3];
|
||||
|
||||
float anchored_rot;
|
||||
|
||||
/* Grab brush */
|
||||
ListBase grab_active_verts[8];
|
||||
float depth;
|
||||
|
||||
/* Adjust brush strength along each axis
|
||||
to adjust for object scaling */
|
||||
float scale[3];
|
||||
} BrushAction;
|
||||
|
||||
typedef struct RectNode {
|
||||
struct RectNode *next, *prev;
|
||||
rcti r;
|
||||
} RectNode;
|
||||
|
||||
/* Used to store to 2D screen coordinates of each vertex in the mesh. */
|
||||
typedef struct ProjVert {
|
||||
short co[2];
|
||||
|
||||
/* Used to mark whether a vertex is inside a rough bounding box
|
||||
containing the brush. */
|
||||
char inside;
|
||||
} ProjVert;
|
||||
|
||||
static Object *active_ob= NULL;
|
||||
|
||||
SculptData *sculpt_data(void)
|
||||
{
|
||||
return NULL; /*XXX: return &G.scene->sculptdata; */
|
||||
}
|
||||
|
||||
static void sculpt_init_session(void);
|
||||
static void init_brushaction(BrushAction *a, short *, short *);
|
||||
static void sculpt_undo_push(const short);
|
||||
|
||||
SculptSession *sculpt_session(void)
|
||||
{
|
||||
if(!sculpt_data()->session)
|
||||
sculpt_init_session();
|
||||
return sculpt_data()->session;
|
||||
}
|
||||
|
||||
/* ===== MEMORY =====
|
||||
*
|
||||
* Allocate/initialize/free data
|
||||
*/
|
||||
|
||||
static void sculpt_init_session(void)
|
||||
{
|
||||
if(sculpt_data()->session)
|
||||
;/*XXX: sculptsession_free(G.scene); */
|
||||
sculpt_data()->session= MEM_callocN(sizeof(SculptSession), "SculptSession");
|
||||
}
|
||||
|
||||
/* vertex_users is an array of Lists that store all the faces that use a
|
||||
particular vertex. vertex_users is in the same order as mesh.mvert */
|
||||
static void calc_vertex_users()
|
||||
{
|
||||
SculptSession *ss= sculpt_session();
|
||||
int i,j;
|
||||
IndexNode *node= NULL;
|
||||
|
||||
sculpt_vertexusers_free(ss);
|
||||
|
||||
/* For efficiency, use vertex_users_mem as a memory pool (may be larger
|
||||
than necessary if mesh has triangles, but only one alloc is needed.) */
|
||||
ss->vertex_users= MEM_callocN(sizeof(ListBase) * ss->totvert, "vertex_users");
|
||||
ss->vertex_users_size= ss->totvert;
|
||||
ss->vertex_users_mem= MEM_callocN(sizeof(IndexNode)*ss->totface*4, "vertex_users_mem");
|
||||
node= ss->vertex_users_mem;
|
||||
|
||||
/* Find the users */
|
||||
for(i=0; i<ss->totface; ++i){
|
||||
for(j=0; j<(ss->mface[i].v4?4:3); ++j, ++node) {
|
||||
node->index=i;
|
||||
BLI_addtail(&ss->vertex_users[((unsigned int*)(&ss->mface[i]))[j]], node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== INTERFACE =====
|
||||
*/
|
||||
|
||||
/* XXX: this can probably removed entirely */
|
||||
#if 0
|
||||
void sculptmode_rem_tex(void *junk0,void *junk1)
|
||||
{
|
||||
MTex *mtex= G.scene->sculptdata.mtex[G.scene->sculptdata.texact];
|
||||
if(mtex) {
|
||||
SculptSession *ss= sculpt_session();
|
||||
if(mtex->tex) mtex->tex->id.us--;
|
||||
MEM_freeN(mtex);
|
||||
G.scene->sculptdata.mtex[G.scene->sculptdata.texact]= NULL;
|
||||
/* Clear brush preview */
|
||||
if(ss->texcache) {
|
||||
MEM_freeN(ss->texcache);
|
||||
ss->texcache= NULL;
|
||||
}
|
||||
BIF_undo_push("Unlink brush texture");
|
||||
allqueue(REDRAWBUTSEDIT, 0);
|
||||
allqueue(REDRAWOOPS, 0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* ===== OPENGL =====
|
||||
*
|
||||
* Simple functions to get data from the GL
|
||||
*/
|
||||
|
||||
/* Store the modelview and projection matrices and viewport. */
|
||||
void init_sculptmatrices()
|
||||
{
|
||||
/* XXX: probably becomes context data?
|
||||
|
||||
SculptSession *ss= sculpt_session();
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glPushMatrix();
|
||||
glMultMatrixf(OBACT->obmat);
|
||||
|
||||
if(!ss->mats)
|
||||
ss->mats = MEM_callocN(sizeof(bglMats), "sculpt bglmats");
|
||||
bgl_get_mats(ss->mats);
|
||||
|
||||
glPopMatrix();
|
||||
*/
|
||||
}
|
||||
|
||||
/* Uses window coordinates (x,y) to find the depth in the GL depth buffer. If
|
||||
available, G.vd->depths is used so that the brush doesn't sculpt on top of
|
||||
itself (G.vd->depths is only updated at the end of a brush stroke.) */
|
||||
float get_depth(short x, short y)
|
||||
{
|
||||
/* XXX
|
||||
float depth;
|
||||
|
||||
if(x<0 || y<0) return 1;
|
||||
if(x>=curarea->winx || y>=curarea->winy) return 1;
|
||||
|
||||
if(G.vd->depths && x<G.vd->depths->w && y<G.vd->depths->h)
|
||||
return G.vd->depths->depths[y*G.vd->depths->w+x];
|
||||
|
||||
x+= curarea->winrct.xmin;
|
||||
y+= curarea->winrct.ymin;
|
||||
|
||||
glReadPixels(x, y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth);
|
||||
|
||||
return depth; */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Uses window coordinates (x,y) and depth component z to find a point in
|
||||
modelspace */
|
||||
void unproject(float out[3], const short x, const short y, const float z)
|
||||
{
|
||||
SculptSession *ss= sculpt_session();
|
||||
double ux, uy, uz;
|
||||
|
||||
gluUnProject(x,y,z, ss->mats->modelview, ss->mats->projection,
|
||||
(GLint *)ss->mats->viewport, &ux, &uy, &uz );
|
||||
out[0] = ux;
|
||||
out[1] = uy;
|
||||
out[2] = uz;
|
||||
}
|
||||
|
||||
/* Convert a point in model coordinates to 2D screen coordinates. */
|
||||
static void projectf(const float v[3], float p[2])
|
||||
{
|
||||
SculptSession *ss= sculpt_session();
|
||||
double ux, uy, uz;
|
||||
|
||||
gluProject(v[0],v[1],v[2], ss->mats->modelview, ss->mats->projection,
|
||||
(GLint *)ss->mats->viewport, &ux, &uy, &uz);
|
||||
p[0]= ux;
|
||||
p[1]= uy;
|
||||
}
|
||||
|
||||
static void project(const float v[3], short p[2])
|
||||
{
|
||||
float f[2];
|
||||
projectf(v, f);
|
||||
|
||||
p[0]= f[0];
|
||||
p[1]= f[1];
|
||||
}
|
||||
|
||||
/* ===== Sculpting =====
|
||||
*
|
||||
*/
|
||||
|
||||
/* Return modified brush size. Uses current tablet pressure (if available) to
|
||||
shrink the brush. Skipped for grab brush because only the first mouse down
|
||||
size is used, which is small if the user has just touched the pen to the
|
||||
tablet */
|
||||
char brush_size()
|
||||
{
|
||||
SculptData *sd = NULL; /* XXX */
|
||||
const BrushData *b= sculptmode_brush();
|
||||
float size= b->size;
|
||||
float pressure= get_pressure();
|
||||
short activedevice= get_activedevice();
|
||||
|
||||
if(sculpt_data()->brush_type!=GRAB_BRUSH) {
|
||||
const float size_factor= sd->tablet_size / 10.0f;
|
||||
|
||||
/* XXX: tablet stuff
|
||||
if(ELEM(activedevice, DEV_STYLUS, DEV_ERASER))
|
||||
size*= sd->tablet_size==0?1:
|
||||
(1-size_factor) + pressure*size_factor;*/
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
/* Return modified brush strength. Includes the direction of the brush, positive
|
||||
values pull vertices, negative values push. Uses tablet pressure and a
|
||||
special multiplier found experimentally to scale the strength factor. */
|
||||
float brush_strength(BrushAction *a)
|
||||
{
|
||||
SculptData *sd = NULL; /* XXX */
|
||||
const BrushData* b= sculptmode_brush();
|
||||
float dir= b->dir==1 ? 1 : -1;
|
||||
float pressure= 1;
|
||||
short activedevice= get_activedevice();
|
||||
float flip= a->flip ? -1:1;
|
||||
float anchored = b->flag & SCULPT_BRUSH_ANCHORED ? 25 : 1;
|
||||
|
||||
const float strength_factor= sd->tablet_strength / 10.0f;
|
||||
|
||||
/* XXX: tablet stuff */
|
||||
#if 0
|
||||
if(ELEM(activedevice, DEV_STYLUS, DEV_ERASER))
|
||||
pressure= sd->sculptdata.tablet_strength==0?1:
|
||||
(1-strength_factor) + get_pressure()*strength_factor;
|
||||
|
||||
/* Flip direction for eraser */
|
||||
if(activedevice==DEV_ERASER)
|
||||
dir= -dir;
|
||||
#endif
|
||||
|
||||
switch(sd->brush_type){
|
||||
case DRAW_BRUSH:
|
||||
case LAYER_BRUSH:
|
||||
return b->strength / 5000.0f * dir * pressure * flip * anchored; /*XXX: not sure why? multiplied by G.vd->grid */;
|
||||
case SMOOTH_BRUSH:
|
||||
return b->strength / 50.0f * pressure * anchored;
|
||||
case PINCH_BRUSH:
|
||||
return b->strength / 1000.0f * dir * pressure * flip * anchored;
|
||||
case GRAB_BRUSH:
|
||||
return 1;
|
||||
case INFLATE_BRUSH:
|
||||
return b->strength / 5000.0f * dir * pressure * flip * anchored;
|
||||
case FLATTEN_BRUSH:
|
||||
return b->strength / 500.0f * pressure * anchored;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* For clipping against a mirror modifier */
|
||||
void sculpt_clip(const BrushAction *a, float *co, const float val[3])
|
||||
{
|
||||
char i;
|
||||
for(i=0; i<3; ++i) {
|
||||
if(a->clip[i] && (fabs(co[i]) <= a->cliptol[i]))
|
||||
co[i]= 0.0f;
|
||||
else
|
||||
co[i]= val[i];
|
||||
}
|
||||
}
|
||||
|
||||
void sculpt_axislock(float *co)
|
||||
{
|
||||
SculptData *sd = sculpt_data();
|
||||
if (sd->axislock == AXISLOCK_X+AXISLOCK_Y+AXISLOCK_Z) return;
|
||||
/* XXX: if(G.vd->twmode == V3D_MANIP_LOCAL) { */
|
||||
if(0) {
|
||||
float mat[3][3], imat[3][3];
|
||||
/* XXX: Mat3CpyMat4(mat, OBACT->obmat); */
|
||||
Mat3Inv(imat, mat);
|
||||
Mat3MulVecfl(mat, co);
|
||||
if (sd->axislock & AXISLOCK_X) co[0] = 0.0;
|
||||
if (sd->axislock & AXISLOCK_Y) co[1] = 0.0;
|
||||
if (sd->axislock & AXISLOCK_Z) co[2] = 0.0;
|
||||
Mat3MulVecfl(imat, co);
|
||||
} else {
|
||||
if (sd->axislock & AXISLOCK_X) co[0] = 0.0;
|
||||
if (sd->axislock & AXISLOCK_Y) co[1] = 0.0;
|
||||
if (sd->axislock & AXISLOCK_Z) co[2] = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
static void add_norm_if(float view_vec[3], float out[3], float out_flip[3], const short no[3])
|
||||
{
|
||||
float fno[3] = {no[0], no[1], no[2]};
|
||||
|
||||
Normalize(fno);
|
||||
|
||||
if((Inpf(view_vec, fno)) > 0) {
|
||||
VecAddf(out, out, fno);
|
||||
} else {
|
||||
VecAddf(out_flip, out_flip, fno); /* out_flip is used when out is {0,0,0} */
|
||||
}
|
||||
}
|
||||
|
||||
/* Currently only for the draw brush; finds average normal for all active
|
||||
vertices */
|
||||
void calc_area_normal(float out[3], const BrushAction *a, const float *outdir, const ListBase* active_verts)
|
||||
{
|
||||
ActiveData *node = active_verts->first;
|
||||
SculptData *sd = sculpt_data();
|
||||
const int view = sd->brush_type==DRAW_BRUSH ? sculptmode_brush()->view : 0;
|
||||
float out_flip[3];
|
||||
|
||||
out[0]=out[1]=out[2] = out_flip[0]=out_flip[1]=out_flip[2] = 0;
|
||||
|
||||
if(sculptmode_brush()->flag & SCULPT_BRUSH_ANCHORED) {
|
||||
for(; node; node = node->next)
|
||||
add_norm_if(((BrushAction*)a)->symm.out, out, out_flip, a->orig_norms[node->Index]);
|
||||
}
|
||||
else {
|
||||
for(; node; node = node->next)
|
||||
add_norm_if(((BrushAction*)a)->symm.out, out, out_flip, sd->session->mvert[node->Index].no);
|
||||
}
|
||||
|
||||
if (out[0]==0.0 && out[1]==0.0 && out[2]==0.0) {
|
||||
VECCOPY(out, out_flip);
|
||||
}
|
||||
|
||||
Normalize(out);
|
||||
|
||||
if(outdir) {
|
||||
out[0] = outdir[0] * view + out[0] * (10-view);
|
||||
out[1] = outdir[1] * view + out[1] * (10-view);
|
||||
out[2] = outdir[2] * view + out[2] * (10-view);
|
||||
}
|
||||
|
||||
Normalize(out);
|
||||
}
|
||||
|
||||
void do_draw_brush(SculptSession *ss, const BrushAction *a, const ListBase* active_verts)
|
||||
{
|
||||
float area_normal[3];
|
||||
ActiveData *node= active_verts->first;
|
||||
|
||||
calc_area_normal(area_normal, a, a->symm.out, active_verts);
|
||||
|
||||
sculpt_axislock(area_normal);
|
||||
|
||||
while(node){
|
||||
float *co= ss->mvert[node->Index].co;
|
||||
|
||||
const float val[3]= {co[0]+area_normal[0]*node->Fade*a->scale[0],
|
||||
co[1]+area_normal[1]*node->Fade*a->scale[1],
|
||||
co[2]+area_normal[2]*node->Fade*a->scale[2]};
|
||||
|
||||
sculpt_clip(a, co, val);
|
||||
|
||||
node= node->next;
|
||||
}
|
||||
}
|
||||
|
||||
/* For the smooth brush, uses the neighboring vertices around vert to calculate
|
||||
a smoothed location for vert. Skips corner vertices (used by only one
|
||||
polygon.) */
|
||||
vec3f neighbor_average(SculptSession *ss, const int vert)
|
||||
{
|
||||
int i, skip= -1, total=0;
|
||||
IndexNode *node= ss->vertex_users[vert].first;
|
||||
vec3f avg= {0,0,0};
|
||||
char ncount= BLI_countlist(&ss->vertex_users[vert]);
|
||||
MFace *f;
|
||||
|
||||
/* Don't modify corner vertices */
|
||||
if(ncount==1) {
|
||||
VecCopyf(&avg.x, ss->mvert[vert].co);
|
||||
return avg;
|
||||
}
|
||||
|
||||
while(node){
|
||||
f= &ss->mface[node->index];
|
||||
|
||||
if(f->v4) {
|
||||
skip= (f->v1==vert?2:
|
||||
f->v2==vert?3:
|
||||
f->v3==vert?0:
|
||||
f->v4==vert?1:-1);
|
||||
}
|
||||
|
||||
for(i=0; i<(f->v4?4:3); ++i) {
|
||||
if(i != skip && (ncount!=2 || BLI_countlist(&ss->vertex_users[(&f->v1)[i]]) <= 2)) {
|
||||
VecAddf(&avg.x, &avg.x, ss->mvert[(&f->v1)[i]].co);
|
||||
++total;
|
||||
}
|
||||
}
|
||||
|
||||
node= node->next;
|
||||
}
|
||||
|
||||
if(total>0) {
|
||||
avg.x/= total;
|
||||
avg.y/= total;
|
||||
avg.z/= total;
|
||||
}
|
||||
else
|
||||
VecCopyf(&avg.x, ss->mvert[vert].co);
|
||||
|
||||
return avg;
|
||||
}
|
||||
|
||||
void do_smooth_brush(SculptSession *ss, const BrushAction *a, const ListBase* active_verts)
|
||||
{
|
||||
ActiveData *node= active_verts->first;
|
||||
|
||||
while(node){
|
||||
float *co= ss->mvert[node->Index].co;
|
||||
const vec3f avg= neighbor_average(ss, node->Index);
|
||||
const float val[3]= {co[0]+(avg.x-co[0])*node->Fade,
|
||||
co[1]+(avg.y-co[1])*node->Fade,
|
||||
co[2]+(avg.z-co[2])*node->Fade};
|
||||
sculpt_clip(a, co, val);
|
||||
node= node->next;
|
||||
}
|
||||
}
|
||||
|
||||
void do_pinch_brush(SculptSession *ss, const BrushAction *a, const ListBase* active_verts)
|
||||
{
|
||||
ActiveData *node= active_verts->first;
|
||||
|
||||
while(node) {
|
||||
float *co= ss->mvert[node->Index].co;
|
||||
const float val[3]= {co[0]+(a->symm.center_3d[0]-co[0])*node->Fade,
|
||||
co[1]+(a->symm.center_3d[1]-co[1])*node->Fade,
|
||||
co[2]+(a->symm.center_3d[2]-co[2])*node->Fade};
|
||||
sculpt_clip(a, co, val);
|
||||
node= node->next;
|
||||
}
|
||||
}
|
||||
|
||||
void do_grab_brush(SculptSession *ss, BrushAction *a)
|
||||
{
|
||||
ActiveData *node= a->grab_active_verts[a->symm.index].first;
|
||||
float add[3];
|
||||
float grab_delta[3];
|
||||
|
||||
VecCopyf(grab_delta, a->symm.grab_delta);
|
||||
sculpt_axislock(grab_delta);
|
||||
|
||||
while(node) {
|
||||
float *co= ss->mvert[node->Index].co;
|
||||
|
||||
VecCopyf(add, grab_delta);
|
||||
VecMulf(add, node->Fade);
|
||||
VecAddf(add, add, co);
|
||||
sculpt_clip(a, co, add);
|
||||
|
||||
node= node->next;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void do_layer_brush(SculptSession *ss, BrushAction *a, const ListBase *active_verts)
|
||||
{
|
||||
float area_normal[3];
|
||||
ActiveData *node= active_verts->first;
|
||||
const float bstr= brush_strength(a);
|
||||
|
||||
calc_area_normal(area_normal, a, NULL, active_verts);
|
||||
|
||||
while(node){
|
||||
float *disp= &a->layer_disps[node->Index];
|
||||
|
||||
if((bstr > 0 && *disp < bstr) ||
|
||||
(bstr < 0 && *disp > bstr)) {
|
||||
float *co= ss->mvert[node->Index].co;
|
||||
|
||||
*disp+= node->Fade;
|
||||
|
||||
if(bstr < 0) {
|
||||
if(*disp < bstr)
|
||||
*disp = bstr;
|
||||
} else {
|
||||
if(*disp > bstr)
|
||||
*disp = bstr;
|
||||
}
|
||||
|
||||
{
|
||||
const float val[3]= {a->mesh_store[node->Index].x+area_normal[0] * *disp*a->scale[0],
|
||||
a->mesh_store[node->Index].y+area_normal[1] * *disp*a->scale[1],
|
||||
a->mesh_store[node->Index].z+area_normal[2] * *disp*a->scale[2]};
|
||||
sculpt_clip(a, co, val);
|
||||
}
|
||||
}
|
||||
|
||||
node= node->next;
|
||||
}
|
||||
}
|
||||
|
||||
void do_inflate_brush(SculptSession *ss, const BrushAction *a, const ListBase *active_verts)
|
||||
{
|
||||
ActiveData *node= active_verts->first;
|
||||
float add[3];
|
||||
|
||||
while(node) {
|
||||
float *co= ss->mvert[node->Index].co;
|
||||
short *no= ss->mvert[node->Index].no;
|
||||
|
||||
add[0]= no[0]/ 32767.0f;
|
||||
add[1]= no[1]/ 32767.0f;
|
||||
add[2]= no[2]/ 32767.0f;
|
||||
VecMulf(add, node->Fade);
|
||||
add[0]*= a->scale[0];
|
||||
add[1]*= a->scale[1];
|
||||
add[2]*= a->scale[2];
|
||||
VecAddf(add, add, co);
|
||||
|
||||
sculpt_clip(a, co, add);
|
||||
|
||||
node= node->next;
|
||||
}
|
||||
}
|
||||
|
||||
void calc_flatten_center(SculptSession *ss, ActiveData *node, float co[3])
|
||||
{
|
||||
ActiveData *outer[FLATTEN_SAMPLE_SIZE];
|
||||
int i;
|
||||
|
||||
for(i = 0; i < FLATTEN_SAMPLE_SIZE; ++i)
|
||||
outer[i] = node;
|
||||
|
||||
for(; node; node = node->next) {
|
||||
for(i = 0; i < FLATTEN_SAMPLE_SIZE; ++i) {
|
||||
if(node->dist > outer[i]->dist) {
|
||||
outer[i] = node;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
co[0] = co[1] = co[2] = 0.0f;
|
||||
for(i = 0; i < FLATTEN_SAMPLE_SIZE; ++i)
|
||||
VecAddf(co, co, ss->mvert[outer[i]->Index].co);
|
||||
VecMulf(co, 1.0f / FLATTEN_SAMPLE_SIZE);
|
||||
}
|
||||
|
||||
void do_flatten_brush(SculptSession *ss, const BrushAction *a, const ListBase *active_verts)
|
||||
{
|
||||
ActiveData *node= active_verts->first;
|
||||
/* area_normal and cntr define the plane towards which vertices are squashed */
|
||||
float area_normal[3];
|
||||
float cntr[3];
|
||||
|
||||
calc_area_normal(area_normal, a, a->symm.out, active_verts);
|
||||
calc_flatten_center(ss, node, cntr);
|
||||
|
||||
while(node){
|
||||
float *co= ss->mvert[node->Index].co;
|
||||
float p1[3], sub1[3], sub2[3], intr[3], val[3];
|
||||
|
||||
/* Find the intersection between squash-plane and vertex (along the area normal) */
|
||||
VecSubf(p1, co, area_normal);
|
||||
VecSubf(sub1, cntr, p1);
|
||||
VecSubf(sub2, co, p1);
|
||||
VecSubf(intr, co, p1);
|
||||
VecMulf(intr, Inpf(area_normal, sub1) / Inpf(area_normal, sub2));
|
||||
VecAddf(intr, intr, p1);
|
||||
|
||||
VecSubf(val, intr, co);
|
||||
VecMulf(val, node->Fade);
|
||||
VecAddf(val, val, co);
|
||||
|
||||
sculpt_clip(a, co, val);
|
||||
|
||||
node= node->next;
|
||||
}
|
||||
}
|
||||
|
||||
/* Uses the brush curve control to find a strength value between 0 and 1 */
|
||||
float curve_strength(float p, const float len)
|
||||
{
|
||||
SculptData *sd = NULL; /* XXX */
|
||||
if(p > len) p= len;
|
||||
return curvemapping_evaluateF(sd->cumap, 0, p/len);
|
||||
}
|
||||
|
||||
/* Uses symm to selectively flip any axis of a coordinate. */
|
||||
void flip_coord(float co[3], const char symm)
|
||||
{
|
||||
if(symm & SYMM_X)
|
||||
co[0]= -co[0];
|
||||
if(symm & SYMM_Y)
|
||||
co[1]= -co[1];
|
||||
if(symm & SYMM_Z)
|
||||
co[2]= -co[2];
|
||||
}
|
||||
|
||||
/* Use the warpfac field in MTex to store a rotation value for sculpt textures. Value is in degrees */
|
||||
float sculpt_tex_angle(void)
|
||||
{
|
||||
SculptData *sd= sculpt_data();
|
||||
if(sd->texact!=-1 && sd->mtex[sd->texact])
|
||||
return sd->mtex[sd->texact]->warpfac;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void set_tex_angle(const float f)
|
||||
{
|
||||
SculptData *sd = sculpt_data();
|
||||
if(sd->texact != -1 && sd->mtex[sd->texact])
|
||||
sd->mtex[sd->texact]->warpfac = f;
|
||||
}
|
||||
|
||||
float to_rad(const float deg)
|
||||
{
|
||||
return deg * (M_PI/180.0f);
|
||||
}
|
||||
|
||||
float to_deg(const float rad)
|
||||
{
|
||||
return rad * (180.0f/M_PI);
|
||||
}
|
||||
|
||||
/* Get a pixel from the texcache at (px, py) */
|
||||
static unsigned char get_texcache_pixel(const SculptSession *ss, int px, int py)
|
||||
{
|
||||
unsigned *p;
|
||||
p = ss->texcache + py * ss->texcache_w + px;
|
||||
return ((unsigned char*)(p))[0];
|
||||
}
|
||||
|
||||
static float get_texcache_pixel_bilinear(const SculptSession *ss, float u, float v)
|
||||
{
|
||||
int x, y, x2, y2;
|
||||
const int tc_max = TC_SIZE - 1;
|
||||
float urat, vrat, uopp;
|
||||
|
||||
if(u < 0) u = 0;
|
||||
else if(u >= TC_SIZE) u = tc_max;
|
||||
if(v < 0) v = 0;
|
||||
else if(v >= TC_SIZE) v = tc_max;
|
||||
|
||||
x = floor(u);
|
||||
y = floor(v);
|
||||
x2 = x + 1;
|
||||
y2 = y + 1;
|
||||
|
||||
if(x2 > TC_SIZE) x2 = tc_max;
|
||||
if(y2 > TC_SIZE) y2 = tc_max;
|
||||
|
||||
urat = u - x;
|
||||
vrat = v - y;
|
||||
uopp = 1 - urat;
|
||||
|
||||
return ((get_texcache_pixel(ss, x, y) * uopp +
|
||||
get_texcache_pixel(ss, x2, y) * urat) * (1 - vrat) +
|
||||
(get_texcache_pixel(ss, x, y2) * uopp +
|
||||
get_texcache_pixel(ss, x2, y2) * urat) * vrat) / 255.0;
|
||||
}
|
||||
|
||||
/* Return a multiplier for brush strength on a particular vertex. */
|
||||
float tex_strength(BrushAction *a, float *point, const float len,const unsigned vindex)
|
||||
{
|
||||
SculptData *sd= sculpt_data();
|
||||
SculptSession *ss= sculpt_session();
|
||||
float avg= 1;
|
||||
|
||||
if(sd->texact==-1 || !sd->mtex[sd->texact])
|
||||
avg= 1;
|
||||
else if(sd->texrept==SCULPTREPT_3D) {
|
||||
/* Get strength by feeding the vertex location directly
|
||||
into a texture */
|
||||
float jnk;
|
||||
const float factor= 0.01;
|
||||
MTex mtex;
|
||||
memset(&mtex,0,sizeof(MTex));
|
||||
mtex.tex= sd->mtex[sd->texact]->tex;
|
||||
mtex.projx= 1;
|
||||
mtex.projy= 2;
|
||||
mtex.projz= 3;
|
||||
VecCopyf(mtex.size, sd->mtex[sd->texact]->size);
|
||||
VecMulf(mtex.size, factor);
|
||||
if(!sd->texsep)
|
||||
mtex.size[1]= mtex.size[2]= mtex.size[0];
|
||||
|
||||
externtex(&mtex,point,&avg,&jnk,&jnk,&jnk,&jnk);
|
||||
}
|
||||
else if(ss->texcache) {
|
||||
const float bsize= a->radius * 2;
|
||||
const float rot= to_rad(sculpt_tex_angle()) + a->anchored_rot;
|
||||
int px, py;
|
||||
float flip[3], point_2d[2];
|
||||
|
||||
/* If the active area is being applied for symmetry, flip it
|
||||
across the symmetry axis in order to project it. This insures
|
||||
that the brush texture will be oriented correctly. */
|
||||
VecCopyf(flip, point);
|
||||
flip_coord(flip, a->symm.index);
|
||||
projectf(flip, point_2d);
|
||||
|
||||
/* For Tile and Drag modes, get the 2D screen coordinates of the
|
||||
and scale them up or down to the texture size. */
|
||||
if(sd->texrept==SCULPTREPT_TILE) {
|
||||
const int sx= (const int)sd->mtex[sd->texact]->size[0];
|
||||
const int sy= (const int)sd->texsep ? sd->mtex[sd->texact]->size[1] : sx;
|
||||
|
||||
float fx= point_2d[0];
|
||||
float fy= point_2d[1];
|
||||
|
||||
float angle= atan2(fy, fx) - rot;
|
||||
float flen= sqrtf(fx*fx + fy*fy);
|
||||
|
||||
if(rot<0.001 && rot>-0.001) {
|
||||
px= point_2d[0];
|
||||
py= point_2d[1];
|
||||
} else {
|
||||
px= flen * cos(angle) + 2000;
|
||||
py= flen * sin(angle) + 2000;
|
||||
}
|
||||
if(sx != 1)
|
||||
px %= sx-1;
|
||||
if(sy != 1)
|
||||
py %= sy-1;
|
||||
avg= get_texcache_pixel_bilinear(ss, TC_SIZE*px/sx, TC_SIZE*py/sy);
|
||||
} else {
|
||||
float fx= (point_2d[0] - a->mouse[0]) / bsize;
|
||||
float fy= (point_2d[1] - a->mouse[1]) / bsize;
|
||||
|
||||
float angle= atan2(fy, fx) - rot;
|
||||
float flen= sqrtf(fx*fx + fy*fy);
|
||||
|
||||
fx = flen * cos(angle) + 0.5;
|
||||
fy = flen * sin(angle) + 0.5;
|
||||
|
||||
avg= get_texcache_pixel_bilinear(ss, fx * TC_SIZE, fy * TC_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
if(sd->texfade)
|
||||
avg*= curve_strength(len, a->size_3d); /* Smooth curve */
|
||||
|
||||
return avg;
|
||||
}
|
||||
|
||||
/* Mark area around the brush as damaged. projverts are marked if they are
|
||||
inside the area and the damaged rectangle in 2D screen coordinates is
|
||||
added to damaged_rects. */
|
||||
void sculpt_add_damaged_rect(BrushAction *a)
|
||||
{
|
||||
short p[2];
|
||||
RectNode *rn= MEM_mallocN(sizeof(RectNode),"RectNode");
|
||||
SculptSession *ss = sculpt_session();
|
||||
const float radius = a->radius > a->prev_radius ? a->radius : a->prev_radius;
|
||||
unsigned i;
|
||||
|
||||
/* Find center */
|
||||
project(a->symm.center_3d, p);
|
||||
rn->r.xmin= p[0] - radius;
|
||||
rn->r.ymin= p[1] - radius;
|
||||
rn->r.xmax= p[0] + radius;
|
||||
rn->r.ymax= p[1] + radius;
|
||||
|
||||
BLI_addtail(&sculpt_session()->damaged_rects, rn);
|
||||
|
||||
/* Update insides */
|
||||
for(i=0; i<ss->totvert; ++i) {
|
||||
if(!ss->projverts[i].inside) {
|
||||
if(ss->projverts[i].co[0] > rn->r.xmin && ss->projverts[i].co[1] > rn->r.ymin &&
|
||||
ss->projverts[i].co[0] < rn->r.xmax && ss->projverts[i].co[1] < rn->r.ymax) {
|
||||
ss->projverts[i].inside= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Clears the depth buffer in each modified area. */
|
||||
void sculpt_clear_damaged_areas(SculptSession *ss)
|
||||
{
|
||||
RectNode *rn= NULL;
|
||||
|
||||
for(rn = ss->damaged_rects.first; rn; rn = rn->next) {
|
||||
rcti clp = rn->r;
|
||||
rcti *win = NULL; /*XXX: &curarea->winrct; */
|
||||
|
||||
clp.xmin += win->xmin;
|
||||
clp.xmax += win->xmin;
|
||||
clp.ymin += win->ymin;
|
||||
clp.ymax += win->ymin;
|
||||
|
||||
if(clp.xmin < win->xmax && clp.xmax > win->xmin &&
|
||||
clp.ymin < win->ymax && clp.ymax > win->ymin) {
|
||||
if(clp.xmin < win->xmin) clp.xmin = win->xmin;
|
||||
if(clp.ymin < win->ymin) clp.ymin = win->ymin;
|
||||
if(clp.xmax > win->xmax) clp.xmax = win->xmax;
|
||||
if(clp.ymax > win->ymax) clp.ymax = win->ymax;
|
||||
|
||||
glScissor(clp.xmin + 1, clp.ymin + 1,
|
||||
clp.xmax - clp.xmin - 2,
|
||||
clp.ymax - clp.ymin - 2);
|
||||
}
|
||||
|
||||
glClear(GL_DEPTH_BUFFER_BIT);
|
||||
}
|
||||
}
|
||||
|
||||
void do_brush_action(BrushAction *a)
|
||||
{
|
||||
int i;
|
||||
float av_dist;
|
||||
ListBase active_verts={0,0};
|
||||
ActiveData *adata= 0;
|
||||
float *vert;
|
||||
Mesh *me= NULL; /*XXX: get_mesh(OBACT); */
|
||||
const float bstrength= brush_strength(a);
|
||||
KeyBlock *keyblock= NULL; /*XXX: ob_get_keyblock(OBACT); */
|
||||
SculptData *sd = sculpt_data();
|
||||
SculptSession *ss = sculpt_session();
|
||||
|
||||
sculpt_add_damaged_rect(a);
|
||||
|
||||
/* Build a list of all vertices that are potentially within the brush's
|
||||
area of influence. Only do this once for the grab brush. */
|
||||
if((sd->brush_type != GRAB_BRUSH) || a->firsttime) {
|
||||
for(i=0; i<ss->totvert; ++i) {
|
||||
/* Projverts.inside provides a rough bounding box */
|
||||
if(ss->multires || ss->projverts[i].inside) {
|
||||
//vert= ss->vertexcosnos ? &ss->vertexcosnos[i*6] : a->verts[i].co;
|
||||
vert= ss->mvert[i].co;
|
||||
av_dist= VecLenf(a->symm.center_3d, vert);
|
||||
if(av_dist < a->size_3d) {
|
||||
adata= (ActiveData*)MEM_mallocN(sizeof(ActiveData), "ActiveData");
|
||||
|
||||
adata->Index = i;
|
||||
/* Fade is used to store the final strength at which the brush
|
||||
should modify a particular vertex. */
|
||||
adata->Fade= tex_strength(a, vert, av_dist, i) * bstrength;
|
||||
adata->dist = av_dist;
|
||||
|
||||
if(sd->brush_type == GRAB_BRUSH && a->firsttime)
|
||||
BLI_addtail(&a->grab_active_verts[a->symm.index], adata);
|
||||
else
|
||||
BLI_addtail(&active_verts, adata);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Only act if some verts are inside the brush area */
|
||||
if(active_verts.first || (sd->brush_type == GRAB_BRUSH && a->grab_active_verts[a->symm.index].first)) {
|
||||
/* Apply one type of brush action */
|
||||
switch(sd->brush_type){
|
||||
case DRAW_BRUSH:
|
||||
do_draw_brush(ss, a, &active_verts);
|
||||
break;
|
||||
case SMOOTH_BRUSH:
|
||||
do_smooth_brush(ss, a, &active_verts);
|
||||
break;
|
||||
case PINCH_BRUSH:
|
||||
do_pinch_brush(ss, a, &active_verts);
|
||||
break;
|
||||
case INFLATE_BRUSH:
|
||||
do_inflate_brush(ss, a, &active_verts);
|
||||
break;
|
||||
case GRAB_BRUSH:
|
||||
do_grab_brush(ss, a);
|
||||
break;
|
||||
case LAYER_BRUSH:
|
||||
do_layer_brush(ss, a, &active_verts);
|
||||
break;
|
||||
case FLATTEN_BRUSH:
|
||||
do_flatten_brush(ss, a, &active_verts);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Copy the modified vertices from mesh to the active key */
|
||||
if(keyblock && !ss->multires) {
|
||||
float *co= keyblock->data;
|
||||
if(co) {
|
||||
if(sd->brush_type == GRAB_BRUSH)
|
||||
adata = a->grab_active_verts[a->symm.index].first;
|
||||
else
|
||||
adata = active_verts.first;
|
||||
|
||||
for(; adata; adata= adata->next)
|
||||
if(adata->Index < keyblock->totelem)
|
||||
VecCopyf(&co[adata->Index*3], me->mvert[adata->Index].co);
|
||||
}
|
||||
}
|
||||
|
||||
if(ss->vertexcosnos && !ss->multires)
|
||||
BLI_freelistN(&active_verts);
|
||||
else {
|
||||
if(sd->brush_type != GRAB_BRUSH)
|
||||
addlisttolist(&ss->damaged_verts, &active_verts);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Flip all the editdata across the axis/axes specified by symm. Used to
|
||||
calculate multiple modifications to the mesh when symmetry is enabled. */
|
||||
void calc_brushdata_symm(BrushAction *a, const char symm)
|
||||
{
|
||||
flip_coord(a->symm.center_3d, symm);
|
||||
flip_coord(a->symm.up, symm);
|
||||
flip_coord(a->symm.right, symm);
|
||||
flip_coord(a->symm.out, symm);
|
||||
|
||||
a->symm.index= symm;
|
||||
|
||||
flip_coord(a->symm.grab_delta, symm);
|
||||
}
|
||||
|
||||
void do_symmetrical_brush_actions(BrushAction *a, short co[2], short pr_co[2])
|
||||
{
|
||||
const char symm = sculpt_data()->symm;
|
||||
BrushActionSymm orig;
|
||||
int i;
|
||||
|
||||
init_brushaction(a, co, pr_co);
|
||||
orig = a->symm;
|
||||
do_brush_action(a);
|
||||
|
||||
for(i = 1; i <= symm; ++i) {
|
||||
if(symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5))) {
|
||||
// Restore the original symmetry data
|
||||
a->symm = orig;
|
||||
|
||||
calc_brushdata_symm(a, i);
|
||||
do_brush_action(a);
|
||||
}
|
||||
}
|
||||
|
||||
a->symm = orig;
|
||||
}
|
||||
|
||||
void add_face_normal(vec3f *norm, MVert *mvert, const MFace* face, float *fn)
|
||||
{
|
||||
vec3f c= {mvert[face->v1].co[0],mvert[face->v1].co[1],mvert[face->v1].co[2]};
|
||||
vec3f b= {mvert[face->v2].co[0],mvert[face->v2].co[1],mvert[face->v2].co[2]};
|
||||
vec3f a= {mvert[face->v3].co[0],mvert[face->v3].co[1],mvert[face->v3].co[2]};
|
||||
vec3f s1, s2;
|
||||
float final[3];
|
||||
|
||||
VecSubf(&s1.x,&a.x,&b.x);
|
||||
VecSubf(&s2.x,&c.x,&b.x);
|
||||
|
||||
final[0] = s1.y * s2.z - s1.z * s2.y;
|
||||
final[1] = s1.z * s2.x - s1.x * s2.z;
|
||||
final[2] = s1.x * s2.y - s1.y * s2.x;
|
||||
|
||||
if(fn)
|
||||
VecCopyf(fn, final);
|
||||
|
||||
norm->x+= final[0];
|
||||
norm->y+= final[1];
|
||||
norm->z+= final[2];
|
||||
}
|
||||
|
||||
void update_damaged_vert(ListBase *lb, BrushAction *a)
|
||||
{
|
||||
ActiveData *vert;
|
||||
SculptSession *ss = sculpt_session();
|
||||
|
||||
for(vert= lb->first; vert; vert= vert->next) {
|
||||
vec3f norm= {0,0,0};
|
||||
IndexNode *face= sculpt_session()->vertex_users[vert->Index].first;
|
||||
|
||||
while(face){
|
||||
float *fn = NULL;
|
||||
if(ss->face_normals)
|
||||
fn = &ss->face_normals[face->index*3];
|
||||
add_face_normal(&norm, ss->mvert, &ss->mface[face->index], fn);
|
||||
face= face->next;
|
||||
}
|
||||
Normalize(&norm.x);
|
||||
|
||||
ss->mvert[vert->Index].no[0]=norm.x*32767;
|
||||
ss->mvert[vert->Index].no[1]=norm.y*32767;
|
||||
ss->mvert[vert->Index].no[2]=norm.z*32767;
|
||||
}
|
||||
}
|
||||
|
||||
void calc_damaged_verts(ListBase *damaged_verts, BrushAction *a)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i=0; i<8; ++i)
|
||||
update_damaged_vert(&a->grab_active_verts[i], a);
|
||||
|
||||
update_damaged_vert(damaged_verts, a);
|
||||
BLI_freelistN(damaged_verts);
|
||||
damaged_verts->first = damaged_verts->last = NULL;
|
||||
}
|
||||
|
||||
void projverts_clear_inside(SculptSession *ss)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < ss->totvert; ++i)
|
||||
ss->projverts[i].inside = 0;
|
||||
}
|
||||
|
||||
BrushData *sculptmode_brush(void)
|
||||
{
|
||||
SculptData *sd= sculpt_data();
|
||||
|
||||
BrushData *bd =
|
||||
(sd->brush_type==DRAW_BRUSH ? &sd->drawbrush :
|
||||
sd->brush_type==SMOOTH_BRUSH ? &sd->smoothbrush :
|
||||
sd->brush_type==PINCH_BRUSH ? &sd->pinchbrush :
|
||||
sd->brush_type==INFLATE_BRUSH ? &sd->inflatebrush :
|
||||
sd->brush_type==GRAB_BRUSH ? &sd->grabbrush :
|
||||
sd->brush_type==LAYER_BRUSH ? &sd->layerbrush :
|
||||
sd->brush_type==FLATTEN_BRUSH ? &sd->flattenbrush : NULL);
|
||||
|
||||
if(!bd) {
|
||||
/* XXX: sculptdata_init(G.scene); */
|
||||
bd = &sd->drawbrush;
|
||||
}
|
||||
|
||||
return bd;
|
||||
}
|
||||
|
||||
void sculptmode_update_tex()
|
||||
{
|
||||
SculptData *sd= sculpt_data();
|
||||
SculptSession *ss= sculpt_session();
|
||||
MTex *mtex = sd->mtex[sd->texact];
|
||||
TexResult texres = {0};
|
||||
float x, y, step=2.0/TC_SIZE, co[3];
|
||||
int hasrgb, ix, iy;
|
||||
|
||||
/* Skip Default brush shape and non-textures */
|
||||
if(sd->texact == -1 || !sd->mtex[sd->texact]) return;
|
||||
|
||||
if(ss->texcache) {
|
||||
MEM_freeN(ss->texcache);
|
||||
ss->texcache= NULL;
|
||||
}
|
||||
|
||||
ss->texcache_w = ss->texcache_h = TC_SIZE;
|
||||
ss->texcache = MEM_callocN(sizeof(int) * ss->texcache_w * ss->texcache_h, "Sculpt Texture cache");
|
||||
|
||||
if(mtex && mtex->tex) {
|
||||
BKE_image_get_ibuf(sd->mtex[sd->texact]->tex->ima, NULL);
|
||||
|
||||
/*do normalized cannonical view coords for texture*/
|
||||
for (y=-1.0, iy=0; iy<TC_SIZE; iy++, y += step) {
|
||||
for (x=-1.0, ix=0; ix<TC_SIZE; ix++, x += step) {
|
||||
co[0]= x;
|
||||
co[1]= y;
|
||||
co[2]= 0.0f;
|
||||
|
||||
/* This is copied from displace modifier code */
|
||||
hasrgb = multitex_ext(mtex->tex, co, NULL, NULL, 1, &texres);
|
||||
|
||||
/* if the texture gave an RGB value, we assume it didn't give a valid
|
||||
* intensity, so calculate one (formula from do_material_tex).
|
||||
* if the texture didn't give an RGB value, copy the intensity across
|
||||
*/
|
||||
if(hasrgb & TEX_RGB)
|
||||
texres.tin = (0.35 * texres.tr + 0.45 *
|
||||
texres.tg + 0.2 * texres.tb);
|
||||
|
||||
texres.tin = texres.tin * 255.0;
|
||||
((char*)ss->texcache)[(iy*TC_SIZE+ix)*4] = (char)texres.tin;
|
||||
((char*)ss->texcache)[(iy*TC_SIZE+ix)*4+1] = (char)texres.tin;
|
||||
((char*)ss->texcache)[(iy*TC_SIZE+ix)*4+2] = (char)texres.tin;
|
||||
((char*)ss->texcache)[(iy*TC_SIZE+ix)*4+3] = (char)texres.tin;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* pr_mouse is only used for the grab brush, can be NULL otherwise */
|
||||
static void init_brushaction(BrushAction *a, short *mouse, short *pr_mouse)
|
||||
{
|
||||
SculptData *sd = sculpt_data();
|
||||
SculptSession *ss = sculpt_session();
|
||||
Object *ob = NULL; /* XXX */
|
||||
const float mouse_depth = get_depth(mouse[0], mouse[1]);
|
||||
float brush_edge_loc[3], zero_loc[3], oldloc[3];
|
||||
ModifierData *md;
|
||||
int i;
|
||||
const char flip = 0; /*XXX: (get_qual() == LR_SHIFTKEY); */
|
||||
const char anchored = sculptmode_brush()->flag & SCULPT_BRUSH_ANCHORED;
|
||||
short orig_mouse[2], dx=0, dy=0;
|
||||
|
||||
a->flip = flip;
|
||||
a->symm.index = 0;
|
||||
|
||||
if(a->firsttime)
|
||||
a->depth = mouse_depth;
|
||||
|
||||
/* Convert the location and size of the brush to
|
||||
modelspace coords */
|
||||
if(a->firsttime || !anchored) {
|
||||
unproject(a->symm.center_3d, mouse[0], mouse[1], mouse_depth);
|
||||
a->mouse[0] = mouse[0];
|
||||
a->mouse[1] = mouse[1];
|
||||
}
|
||||
|
||||
if(anchored) {
|
||||
project(a->symm.center_3d, orig_mouse);
|
||||
dx = mouse[0] - orig_mouse[0];
|
||||
dy = mouse[1] - orig_mouse[1];
|
||||
}
|
||||
|
||||
if(anchored) {
|
||||
unproject(brush_edge_loc, mouse[0], mouse[1], a->depth);
|
||||
a->anchored_rot = atan2(dy, dx);
|
||||
}
|
||||
else
|
||||
unproject(brush_edge_loc, mouse[0] + brush_size(), mouse[1], mouse_depth);
|
||||
|
||||
a->size_3d = VecLenf(a->symm.center_3d, brush_edge_loc);
|
||||
|
||||
a->prev_radius = a->radius;
|
||||
|
||||
if(anchored)
|
||||
a->radius = sqrt(dx*dx + dy*dy);
|
||||
else
|
||||
a->radius = brush_size();
|
||||
|
||||
/* Set the pivot to allow the model to rotate around the center of the brush */
|
||||
if(get_depth(mouse[0],mouse[1]) < 1.0)
|
||||
VecCopyf(sd->pivot, a->symm.center_3d);
|
||||
|
||||
/* Now project the Up, Right, and Out normals from view to model coords */
|
||||
unproject(zero_loc, 0, 0, 0);
|
||||
unproject(a->symm.up, 0, -1, 0);
|
||||
unproject(a->symm.right, 1, 0, 0);
|
||||
unproject(a->symm.out, 0, 0, -1);
|
||||
VecSubf(a->symm.up, a->symm.up, zero_loc);
|
||||
VecSubf(a->symm.right, a->symm.right, zero_loc);
|
||||
VecSubf(a->symm.out, a->symm.out, zero_loc);
|
||||
Normalize(a->symm.up);
|
||||
Normalize(a->symm.right);
|
||||
Normalize(a->symm.out);
|
||||
|
||||
/* Initialize mirror modifier clipping */
|
||||
for(i=0; i<3; ++i) {
|
||||
a->clip[i]= 0;
|
||||
a->cliptol[i]= 0;
|
||||
}
|
||||
for(md= ob->modifiers.first; md; md= md->next) {
|
||||
if(md->type==eModifierType_Mirror && (md->mode & eModifierMode_Realtime)) {
|
||||
const MirrorModifierData *mmd = (MirrorModifierData*) md;
|
||||
|
||||
if(mmd->flag & MOD_MIR_CLIPPING) {
|
||||
a->clip[mmd->axis]= 1;
|
||||
if(mmd->tolerance > a->cliptol[mmd->axis])
|
||||
a->cliptol[mmd->axis] = mmd->tolerance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(sd->brush_type == GRAB_BRUSH) {
|
||||
float gcenter[3];
|
||||
|
||||
/* Find the delta */
|
||||
unproject(gcenter, mouse[0], mouse[1], a->depth);
|
||||
unproject(oldloc, pr_mouse[0], pr_mouse[1], a->depth);
|
||||
VecSubf(a->symm.grab_delta, gcenter, oldloc);
|
||||
}
|
||||
else if(sd->brush_type == LAYER_BRUSH) {
|
||||
if(!a->layer_disps)
|
||||
a->layer_disps= MEM_callocN(sizeof(float)*ss->totvert,"Layer disps");
|
||||
}
|
||||
|
||||
if(sd->brush_type == LAYER_BRUSH || anchored) {
|
||||
unsigned i;
|
||||
|
||||
if(!a->mesh_store) {
|
||||
a->mesh_store= MEM_mallocN(sizeof(vec3f) * ss->totvert, "Sculpt mesh store");
|
||||
for(i = 0; i < ss->totvert; ++i)
|
||||
VecCopyf(&a->mesh_store[i].x, ss->mvert[i].co);
|
||||
}
|
||||
|
||||
if(anchored && a->layer_disps)
|
||||
memset(a->layer_disps, 0, sizeof(float) * ss->totvert);
|
||||
|
||||
if(anchored && !a->orig_norms) {
|
||||
a->orig_norms= MEM_mallocN(sizeof(short) * 3 * ss->totvert, "Sculpt orig norm");
|
||||
for(i = 0; i < ss->totvert; ++i) {
|
||||
a->orig_norms[i][0] = ss->mvert[i].no[0];
|
||||
a->orig_norms[i][1] = ss->mvert[i].no[1];
|
||||
a->orig_norms[i][2] = ss->mvert[i].no[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void sculptmode_set_strength(const int delta)
|
||||
{
|
||||
int val = sculptmode_brush()->strength + delta;
|
||||
if(val < 1) val = 1;
|
||||
if(val > 100) val = 100;
|
||||
sculptmode_brush()->strength= val;
|
||||
}
|
||||
|
||||
/* XXX: haven't brought in the radial control files, not sure where to put them. Note that all the paint modes should have access to radial control! */
|
||||
#if 0
|
||||
static void sculpt_radialcontrol_callback(const int mode, const int val)
|
||||
{
|
||||
SculptSession *ss = sculpt_session();
|
||||
BrushData *br = sculptmode_brush();
|
||||
|
||||
if(mode == RADIALCONTROL_SIZE)
|
||||
br->size = val;
|
||||
else if(mode == RADIALCONTROL_STRENGTH)
|
||||
br->strength = val;
|
||||
else if(mode == RADIALCONTROL_ROTATION)
|
||||
set_tex_angle(val);
|
||||
|
||||
ss->radialcontrol = NULL;
|
||||
}
|
||||
|
||||
/* Returns GL handle to brush texture */
|
||||
static GLuint sculpt_radialcontrol_calctex()
|
||||
{
|
||||
SculptData *sd= sculpt_data();
|
||||
SculptSession *ss= sculpt_session();
|
||||
int i, j;
|
||||
const int tsz = TC_SIZE;
|
||||
float *texdata= MEM_mallocN(sizeof(float)*tsz*tsz, "Brush preview");
|
||||
GLuint tex;
|
||||
|
||||
if(sd->texrept!=SCULPTREPT_3D)
|
||||
sculptmode_update_tex();
|
||||
for(i=0; i<tsz; ++i)
|
||||
for(j=0; j<tsz; ++j) {
|
||||
float magn= sqrt(pow(i-tsz/2,2)+pow(j-tsz/2,2));
|
||||
if(sd->texfade)
|
||||
texdata[i*tsz+j]= curve_strength(magn,tsz/2);
|
||||
else
|
||||
texdata[i*tsz+j]= magn < tsz/2 ? 1 : 0;
|
||||
}
|
||||
if(sd->texact != -1 && ss->texcache) {
|
||||
for(i=0; i<tsz; ++i)
|
||||
for(j=0; j<tsz; ++j) {
|
||||
const int col= ss->texcache[i*tsz+j];
|
||||
texdata[i*tsz+j]*= (((char*)&col)[0]+((char*)&col)[1]+((char*)&col)[2])/3.0f/255.0f;
|
||||
}
|
||||
}
|
||||
|
||||
glGenTextures(1, &tex);
|
||||
glBindTexture(GL_TEXTURE_2D, tex);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, tsz, tsz, 0, GL_ALPHA, GL_FLOAT, texdata);
|
||||
MEM_freeN(texdata);
|
||||
|
||||
return tex;
|
||||
}
|
||||
|
||||
void sculpt_radialcontrol_start(int mode)
|
||||
{
|
||||
SculptData *sd = sculpt_data();
|
||||
SculptSession *ss = sculpt_session();
|
||||
BrushData *br = sculptmode_brush();
|
||||
int orig=1, max=100;
|
||||
|
||||
if(mode == RADIALCONTROL_SIZE) {
|
||||
orig = br->size;
|
||||
max = 200;
|
||||
}
|
||||
else if(mode == RADIALCONTROL_STRENGTH) {
|
||||
orig = br->strength;
|
||||
max = 100;
|
||||
}
|
||||
else if(mode == RADIALCONTROL_ROTATION) {
|
||||
if(sd->texact!=-1 && sd->mtex[sd->texact]) {
|
||||
orig = sculpt_tex_angle();
|
||||
max = 360;
|
||||
}
|
||||
else
|
||||
mode = RADIALCONTROL_NONE;
|
||||
}
|
||||
|
||||
if(mode != RADIALCONTROL_NONE) {
|
||||
ss->radialcontrol= radialcontrol_start(mode, sculpt_radialcontrol_callback, orig, max,
|
||||
sculpt_radialcontrol_calctex());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* XXX: drawing code to go elsewhere!
|
||||
void sculpt_paint_brush(char clear)
|
||||
{
|
||||
if(sculpt_data()->flags & SCULPT_DRAW_BRUSH) {
|
||||
static short mvalo[2];
|
||||
short mval[2];
|
||||
const short rad= sculptmode_brush()->size;
|
||||
|
||||
getmouseco_areawin(mval);
|
||||
|
||||
persp(PERSP_WIN);
|
||||
if(clear)
|
||||
fdrawXORcirc(mval[0], mval[1], rad);
|
||||
else
|
||||
draw_sel_circle(mval, mvalo, rad, rad, 0);
|
||||
|
||||
mvalo[0]= mval[0];
|
||||
mvalo[1]= mval[1];
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
void sculptmode_selectbrush_menu(void)
|
||||
{
|
||||
/* XXX: I guess menus belong elsewhere too?
|
||||
|
||||
SculptData *sd= sculpt_data();
|
||||
int val;
|
||||
|
||||
pupmenu_set_active(sd->brush_type);
|
||||
|
||||
val= pupmenu("Select Brush%t|Draw|Smooth|Pinch|Inflate|Grab|Layer|Flatten");
|
||||
|
||||
if(val>0) {
|
||||
sd->brush_type= val;
|
||||
|
||||
BIF_undo_push("Brush type");
|
||||
|
||||
allqueue(REDRAWVIEW3D, 1);
|
||||
allqueue(REDRAWBUTSEDIT, 1);
|
||||
}*/
|
||||
}
|
||||
|
||||
void sculptmode_update_all_projverts(float *vertcosnos)
|
||||
{
|
||||
SculptSession *ss = sculpt_session();
|
||||
unsigned i;
|
||||
|
||||
if(!ss->projverts)
|
||||
ss->projverts = MEM_mallocN(sizeof(ProjVert)*ss->totvert,"ProjVerts");
|
||||
|
||||
for(i=0; i<ss->totvert; ++i) {
|
||||
project(vertcosnos ? &vertcosnos[i * 6] : ss->mvert[i].co, ss->projverts[i].co);
|
||||
ss->projverts[i].inside= 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Checks whether full update mode (slower) needs to be used to work with modifiers */
|
||||
char sculpt_modifiers_active(Object *ob)
|
||||
{
|
||||
ModifierData *md;
|
||||
|
||||
for(md= modifiers_getVirtualModifierList(ob); md; md= md->next) {
|
||||
if(md->mode & eModifierMode_Realtime && md->type != eModifierType_Multires)
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Sculpt mode handles multires differently from regular meshes, but only if
|
||||
it's the last modifier on the stack and it is not on the first level */
|
||||
struct MultiresModifierData *sculpt_multires_active(Object *ob)
|
||||
{
|
||||
ModifierData *md;
|
||||
|
||||
for(md= modifiers_getVirtualModifierList(ob); md; md= md->next) {
|
||||
if(md->type == eModifierType_Multires && !md->next) {
|
||||
MultiresModifierData *mmd = (MultiresModifierData*)md;
|
||||
if(mmd->lvl != 1)
|
||||
return mmd;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void sculpt_update_mesh_elements(SculptSession *ss, Object *ob)
|
||||
{
|
||||
if(sculpt_multires_active(ob)) {
|
||||
DerivedMesh *dm = mesh_get_derived_final(NULL, ob, CD_MASK_BAREMESH); /* XXX scene=? */
|
||||
ss->multires = 1;
|
||||
ss->totvert = dm->getNumVerts(dm);
|
||||
ss->totface = dm->getNumFaces(dm);
|
||||
ss->mvert = dm->getVertDataArray(dm, CD_MVERT);
|
||||
ss->mface = dm->getFaceDataArray(dm, CD_MFACE);
|
||||
ss->face_normals = dm->getFaceDataArray(dm, CD_NORMAL);
|
||||
}
|
||||
else {
|
||||
Mesh *me = get_mesh(ob);
|
||||
ss->multires = 0;
|
||||
ss->totvert = me->totvert;
|
||||
ss->totface = me->totface;
|
||||
ss->mvert = me->mvert;
|
||||
ss->mface = me->mface;
|
||||
ss->face_normals = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* XXX: lots of drawing code (partial redraw), has to go elsewhere */
|
||||
#if 0
|
||||
void sculptmode_draw_wires(SculptSession *ss, int only_damaged)
|
||||
{
|
||||
Mesh *me = get_mesh(OBACT);
|
||||
int i;
|
||||
|
||||
bglPolygonOffset(1.0);
|
||||
glDepthMask(0);
|
||||
BIF_ThemeColor((OBACT==OBACT)?TH_ACTIVE:TH_SELECT);
|
||||
|
||||
for(i=0; i<me->totedge; i++) {
|
||||
MEdge *med= &me->medge[i];
|
||||
|
||||
if((!only_damaged || (ss->projverts[med->v1].inside || ss->projverts[med->v2].inside)) &&
|
||||
(med->flag & ME_EDGEDRAW)) {
|
||||
glDrawElements(GL_LINES, 2, GL_UNSIGNED_INT, &med->v1);
|
||||
}
|
||||
}
|
||||
|
||||
glDepthMask(1);
|
||||
bglPolygonOffset(0.0);
|
||||
}
|
||||
|
||||
void sculptmode_draw_mesh(int only_damaged)
|
||||
{
|
||||
int i, j, dt, drawCurrentMat = 1, matnr= -1;
|
||||
SculptSession *ss = sculpt_session();
|
||||
|
||||
sculpt_update_mesh_elements(ss, OBACT);
|
||||
|
||||
persp(PERSP_VIEW);
|
||||
mymultmatrix(OBACT->obmat);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glEnable(GL_LIGHTING);
|
||||
/* XXX: GPU_set_object_materials(G.scene, OBACT, 0, NULL); */
|
||||
glEnable(GL_CULL_FACE);
|
||||
|
||||
glShadeModel(GL_SMOOTH);
|
||||
|
||||
glVertexPointer(3, GL_FLOAT, sizeof(MVert), &ss->mvert[0].co);
|
||||
glNormalPointer(GL_SHORT, sizeof(MVert), &ss->mvert[0].no);
|
||||
|
||||
dt= MIN2(G.vd->drawtype, OBACT->dt);
|
||||
if(dt==OB_WIRE)
|
||||
glColorMask(0,0,0,0);
|
||||
|
||||
for(i=0; i<ss->totface; ++i) {
|
||||
MFace *f= &ss->mface[i];
|
||||
char inside= 0;
|
||||
int new_matnr= f->mat_nr + 1;
|
||||
|
||||
if(new_matnr != matnr)
|
||||
drawCurrentMat= GPU_enable_material(matnr = new_matnr, NULL);
|
||||
|
||||
/* If only_damaged!=0, only draw faces that are partially
|
||||
inside the area(s) modified by the brush */
|
||||
if(only_damaged) {
|
||||
for(j=0; j<(f->v4?4:3); ++j) {
|
||||
if(ss->projverts[*((&f->v1)+j)].inside) {
|
||||
inside= 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
inside= 1;
|
||||
|
||||
if(inside && drawCurrentMat)
|
||||
glDrawElements(f->v4?GL_QUADS:GL_TRIANGLES, f->v4?4:3, GL_UNSIGNED_INT, &f->v1);
|
||||
}
|
||||
|
||||
glDisable(GL_CULL_FACE);
|
||||
glDisable(GL_LIGHTING);
|
||||
glColorMask(1,1,1,1);
|
||||
|
||||
if(dt==OB_WIRE || (OBACT->dtx & OB_DRAWWIRE))
|
||||
sculptmode_draw_wires(ss, only_damaged);
|
||||
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
}
|
||||
#endif
|
||||
|
||||
void sculptmode_correct_state(void)
|
||||
{
|
||||
if(!sculpt_session())
|
||||
sculpt_init_session();
|
||||
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glEnableClientState(GL_NORMAL_ARRAY);
|
||||
|
||||
if(!sculpt_session()->vertex_users) calc_vertex_users();
|
||||
}
|
||||
|
||||
void sculpt(void)
|
||||
{
|
||||
SculptData *sd= sculpt_data();
|
||||
SculptSession *ss= sculpt_session();
|
||||
Object *ob= NULL; /*XXX */
|
||||
Mesh *me;
|
||||
MultiresModifierData *mmd = NULL;
|
||||
/* lastSigMouse is for the rake, to store the last place the mouse movement was significant */
|
||||
short mouse[2], mvalo[2], lastSigMouse[2],firsttime=1, mousebut;
|
||||
short modifier_calculations= 0;
|
||||
BrushAction *a = MEM_callocN(sizeof(BrushAction), "brush action");
|
||||
short spacing= 32000;
|
||||
int scissor_box[4];
|
||||
float offsetRot;
|
||||
int smooth_stroke = 0, i;
|
||||
int anchored;
|
||||
|
||||
/* XXX: checking that sculpting is allowed
|
||||
if(!(G.f & G_SCULPTMODE) || G.obedit || !ob || ob->id.lib || !get_mesh(ob) || (get_mesh(ob)->totface == 0))
|
||||
return;
|
||||
if(!(ob->lay & G.vd->lay))
|
||||
error("Active object is not in this layer");
|
||||
if(ob_get_keyblock(ob)) {
|
||||
if(!(ob->shapeflag & OB_SHAPE_LOCK)) {
|
||||
error("Cannot sculpt on unlocked shape key");
|
||||
return;
|
||||
}
|
||||
}*/
|
||||
|
||||
if(!ss) {
|
||||
sculpt_init_session();
|
||||
ss= sd->session;
|
||||
}
|
||||
|
||||
anchored = sculptmode_brush()->flag & SCULPT_BRUSH_ANCHORED;
|
||||
smooth_stroke = (sd->flags & SCULPT_INPUT_SMOOTH) && (sd->brush_type != GRAB_BRUSH) && !anchored;
|
||||
|
||||
if(smooth_stroke)
|
||||
sculpt_stroke_new(256);
|
||||
|
||||
ss->damaged_rects.first = ss->damaged_rects.last = NULL;
|
||||
ss->damaged_verts.first = ss->damaged_verts.last = NULL;
|
||||
ss->vertexcosnos = NULL;
|
||||
|
||||
mmd = sculpt_multires_active(ob);
|
||||
sculpt_update_mesh_elements(ss, ob);
|
||||
|
||||
/* Check that vertex users are up-to-date */
|
||||
if(ob != active_ob || !ss->vertex_users || ss->vertex_users_size != ss->totvert) {
|
||||
sculpt_vertexusers_free(ss);
|
||||
calc_vertex_users();
|
||||
if(ss->projverts)
|
||||
MEM_freeN(ss->projverts);
|
||||
ss->projverts = NULL;
|
||||
active_ob= ob;
|
||||
}
|
||||
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glEnableClientState(GL_NORMAL_ARRAY);
|
||||
|
||||
/*XXX:
|
||||
persp(PERSP_VIEW);
|
||||
getmouseco_areawin(mvalo);*/
|
||||
|
||||
/* Init texture
|
||||
FIXME: Shouldn't be doing this every time! */
|
||||
if(sd->texrept!=SCULPTREPT_3D)
|
||||
sculptmode_update_tex();
|
||||
|
||||
getmouseco_areawin(mouse);
|
||||
mvalo[0]= mouse[0];
|
||||
mvalo[1]= mouse[1];
|
||||
lastSigMouse[0]=mouse[0];
|
||||
lastSigMouse[1]=mouse[1];
|
||||
mousebut = 0; /* XXX: L_MOUSE; */
|
||||
|
||||
/* If modifier_calculations is true, then extra time must be spent
|
||||
updating the mesh. This takes a *lot* longer, so it's worth
|
||||
skipping if the modifier stack is empty. */
|
||||
modifier_calculations= sculpt_modifiers_active(ob);
|
||||
|
||||
init_sculptmatrices();
|
||||
|
||||
if(modifier_calculations)
|
||||
ss->vertexcosnos= mesh_get_mapped_verts_nors(NULL, ob); /* XXX: scene = ? */
|
||||
sculptmode_update_all_projverts(ss->vertexcosnos);
|
||||
|
||||
/* Set scaling adjustment */
|
||||
a->scale[0]= 1.0f / ob->size[0];
|
||||
a->scale[1]= 1.0f / ob->size[1];
|
||||
a->scale[2]= 1.0f / ob->size[2];
|
||||
|
||||
/* Capture original copy */
|
||||
if(sd->flags & SCULPT_DRAW_FAST)
|
||||
glAccum(GL_LOAD, 1);
|
||||
|
||||
/* Get original scissor box */
|
||||
glGetIntegerv(GL_SCISSOR_BOX, scissor_box);
|
||||
|
||||
/* For raking, get the original angle*/
|
||||
offsetRot=sculpt_tex_angle();
|
||||
|
||||
me = get_mesh(ob);
|
||||
|
||||
while (get_mbut() & mousebut) {
|
||||
getmouseco_areawin(mouse);
|
||||
/* If rake, and the mouse has moved over 10 pixels (euclidean) (prevents jitter) then get the new angle */
|
||||
if (sd->rake && (pow(lastSigMouse[0]-mouse[0],2)+pow(lastSigMouse[1]-mouse[1],2))>100){
|
||||
/*Nasty looking, but just orig + new angle really*/
|
||||
set_tex_angle(offsetRot+180.+to_deg(atan2((float)(mouse[1]-lastSigMouse[1]),(float)(mouse[0]-lastSigMouse[0]))));
|
||||
lastSigMouse[0]=mouse[0];
|
||||
lastSigMouse[1]=mouse[1];
|
||||
}
|
||||
|
||||
if(firsttime || mouse[0]!=mvalo[0] || mouse[1]!=mvalo[1] ||
|
||||
sculptmode_brush()->flag & SCULPT_BRUSH_AIRBRUSH) {
|
||||
a->firsttime = firsttime;
|
||||
firsttime= 0;
|
||||
|
||||
if(smooth_stroke)
|
||||
sculpt_stroke_add_point(mouse[0], mouse[1]);
|
||||
|
||||
spacing+= sqrt(pow(mvalo[0]-mouse[0],2)+pow(mvalo[1]-mouse[1],2));
|
||||
|
||||
if(modifier_calculations && !ss->vertexcosnos)
|
||||
ss->vertexcosnos= mesh_get_mapped_verts_nors(NULL, ob); /*XXX scene = ? */
|
||||
|
||||
if(sd->brush_type != GRAB_BRUSH) {
|
||||
if(anchored) {
|
||||
/* Restore the mesh before continuing with anchored stroke */
|
||||
if(a->mesh_store) {
|
||||
for(i = 0; i < ss->totvert; ++i) {
|
||||
VecCopyf(ss->mvert[i].co, &a->mesh_store[i].x);
|
||||
ss->mvert[i].no[0] = a->orig_norms[i][0];
|
||||
ss->mvert[i].no[1] = a->orig_norms[i][1];
|
||||
ss->mvert[i].no[2] = a->orig_norms[i][2];
|
||||
}
|
||||
}
|
||||
|
||||
do_symmetrical_brush_actions(a, mouse, NULL);
|
||||
}
|
||||
else {
|
||||
if(smooth_stroke) {
|
||||
sculpt_stroke_apply(a);
|
||||
}
|
||||
else if(sd->spacing==0 || spacing>sd->spacing) {
|
||||
do_symmetrical_brush_actions(a, mouse, NULL);
|
||||
spacing= 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
do_symmetrical_brush_actions(a, mouse, mvalo);
|
||||
unproject(sd->pivot, mouse[0], mouse[1], a->depth);
|
||||
}
|
||||
|
||||
if((!ss->multires && modifier_calculations) || ob_get_keyblock(ob))
|
||||
;/* XXX: DAG_object_flush_update(G.scene, ob, OB_RECALC_DATA); */
|
||||
|
||||
if(modifier_calculations || sd->brush_type == GRAB_BRUSH || !(sd->flags & SCULPT_DRAW_FAST)) {
|
||||
calc_damaged_verts(&ss->damaged_verts, a);
|
||||
/*XXX: scrarea_do_windraw(curarea); */
|
||||
screen_swapbuffers();
|
||||
} else { /* Optimized drawing */
|
||||
calc_damaged_verts(&ss->damaged_verts, a);
|
||||
|
||||
/* Draw the stored image to the screen */
|
||||
glAccum(GL_RETURN, 1);
|
||||
|
||||
sculpt_clear_damaged_areas(ss);
|
||||
|
||||
/* Draw all the polygons that are inside the modified area(s) */
|
||||
glScissor(scissor_box[0], scissor_box[1], scissor_box[2], scissor_box[3]);
|
||||
sculptmode_draw_mesh(1);
|
||||
glAccum(GL_LOAD, 1);
|
||||
|
||||
projverts_clear_inside(ss);
|
||||
|
||||
/* XXX: persp(PERSP_WIN); */
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
|
||||
/* Draw cursor */
|
||||
if(sculpt_data()->flags & SCULPT_DRAW_BRUSH)
|
||||
fdrawXORcirc((float)mouse[0],(float)mouse[1],sculptmode_brush()->size);
|
||||
if(smooth_stroke)
|
||||
sculpt_stroke_draw();
|
||||
|
||||
myswapbuffers();
|
||||
}
|
||||
|
||||
BLI_freelistN(&ss->damaged_rects);
|
||||
ss->damaged_rects.first = ss->damaged_rects.last = NULL;
|
||||
|
||||
mvalo[0]= mouse[0];
|
||||
mvalo[1]= mouse[1];
|
||||
|
||||
if(ss->vertexcosnos) {
|
||||
MEM_freeN(ss->vertexcosnos);
|
||||
ss->vertexcosnos= NULL;
|
||||
}
|
||||
|
||||
}
|
||||
else BIF_wait_for_statechange();
|
||||
}
|
||||
|
||||
/* Set the rotation of the brush back to what it was before any rake */
|
||||
set_tex_angle(offsetRot);
|
||||
|
||||
if(smooth_stroke) {
|
||||
sculpt_stroke_apply_all(a);
|
||||
calc_damaged_verts(&ss->damaged_verts, a);
|
||||
BLI_freelistN(&ss->damaged_rects);
|
||||
}
|
||||
|
||||
if(a->layer_disps) MEM_freeN(a->layer_disps);
|
||||
if(a->mesh_store) MEM_freeN(a->mesh_store);
|
||||
if(a->orig_norms) MEM_freeN(a->orig_norms);
|
||||
for(i=0; i<8; ++i)
|
||||
BLI_freelistN(&a->grab_active_verts[i]);
|
||||
MEM_freeN(a);
|
||||
sculpt_stroke_free();
|
||||
|
||||
if(mmd) {
|
||||
if(mmd->undo_verts && mmd->undo_verts != ss->mvert)
|
||||
MEM_freeN(mmd->undo_verts);
|
||||
|
||||
mmd->undo_verts = ss->mvert;
|
||||
mmd->undo_verts_tot = ss->totvert;
|
||||
}
|
||||
|
||||
/* XXX: sculpt_undo_push(G.scene->sculptdata.brush_type); */
|
||||
|
||||
/* XXX: if(G.vd->depths) G.vd->depths->damaged= 1;
|
||||
allqueue(REDRAWVIEW3D, 0); */
|
||||
}
|
||||
|
||||
static void sculpt_undo_push(const short brush_type)
|
||||
{
|
||||
switch(brush_type) {
|
||||
case DRAW_BRUSH:
|
||||
BIF_undo_push("Draw Brush"); break;
|
||||
case SMOOTH_BRUSH:
|
||||
BIF_undo_push("Smooth Brush"); break;
|
||||
case PINCH_BRUSH:
|
||||
BIF_undo_push("Pinch Brush"); break;
|
||||
case INFLATE_BRUSH:
|
||||
BIF_undo_push("Inflate Brush"); break;
|
||||
case GRAB_BRUSH:
|
||||
BIF_undo_push("Grab Brush"); break;
|
||||
case LAYER_BRUSH:
|
||||
BIF_undo_push("Layer Brush"); break;
|
||||
case FLATTEN_BRUSH:
|
||||
BIF_undo_push("Flatten Brush"); break;
|
||||
default:
|
||||
BIF_undo_push("Sculpting"); break;
|
||||
}
|
||||
}
|
||||
|
||||
void set_sculptmode(void)
|
||||
{
|
||||
if(G.f & G_SCULPTMODE) {
|
||||
Object *ob = NULL; /*XXX: OBACT; */
|
||||
Mesh *me= get_mesh(ob);
|
||||
|
||||
multires_force_update(ob);
|
||||
|
||||
G.f &= ~G_SCULPTMODE;
|
||||
|
||||
/* XXX: sculptsession_free(G.scene); */
|
||||
if(me && me->pv)
|
||||
mesh_pmv_off(ob, me);
|
||||
}
|
||||
else {
|
||||
G.f |= G_SCULPTMODE;
|
||||
|
||||
/* Called here to sanity-check the brush */
|
||||
sculptmode_brush();
|
||||
|
||||
sculpt_init_session();
|
||||
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glEnableClientState(GL_NORMAL_ARRAY);
|
||||
}
|
||||
|
||||
active_ob= NULL;
|
||||
}
|
||||
|
||||
/* Partial Mesh Visibility */
|
||||
|
||||
/* mode: 0=hide outside selection, 1=hide inside selection */
|
||||
static void sculptmode_do_pmv(Object *ob, rcti *hb_2d, int mode)
|
||||
{
|
||||
Mesh *me= get_mesh(ob);
|
||||
float hidebox[6][3];
|
||||
vec3f plane_normals[4];
|
||||
float plane_ds[4];
|
||||
unsigned i, j;
|
||||
unsigned ndx_show, ndx_hide;
|
||||
MVert *nve;
|
||||
unsigned face_cnt_show= 0, face_ndx_show= 0;
|
||||
unsigned edge_cnt_show= 0, edge_ndx_show= 0;
|
||||
unsigned *old_map= NULL;
|
||||
const unsigned SHOW= 0, HIDE=1;
|
||||
|
||||
/* Convert hide box from 2D to 3D */
|
||||
unproject(hidebox[0], hb_2d->xmin, hb_2d->ymax, 1);
|
||||
unproject(hidebox[1], hb_2d->xmax, hb_2d->ymax, 1);
|
||||
unproject(hidebox[2], hb_2d->xmax, hb_2d->ymin, 1);
|
||||
unproject(hidebox[3], hb_2d->xmin, hb_2d->ymin, 1);
|
||||
unproject(hidebox[4], hb_2d->xmin, hb_2d->ymax, 0);
|
||||
unproject(hidebox[5], hb_2d->xmax, hb_2d->ymin, 0);
|
||||
|
||||
/* Calculate normals for each side of hide box */
|
||||
CalcNormFloat(hidebox[0], hidebox[1], hidebox[4], &plane_normals[0].x);
|
||||
CalcNormFloat(hidebox[1], hidebox[2], hidebox[5], &plane_normals[1].x);
|
||||
CalcNormFloat(hidebox[2], hidebox[3], hidebox[5], &plane_normals[2].x);
|
||||
CalcNormFloat(hidebox[3], hidebox[0], hidebox[4], &plane_normals[3].x);
|
||||
|
||||
/* Calculate D for each side of hide box */
|
||||
for(i= 0; i<4; ++i)
|
||||
plane_ds[i]= hidebox[i][0]*plane_normals[i].x + hidebox[i][1]*plane_normals[i].y +
|
||||
hidebox[i][2]*plane_normals[i].z;
|
||||
|
||||
/* Add partial visibility to mesh */
|
||||
if(!me->pv) {
|
||||
me->pv= MEM_callocN(sizeof(PartialVisibility),"PartialVisibility");
|
||||
} else {
|
||||
old_map= MEM_callocN(sizeof(unsigned)*me->pv->totvert,"PMV oldmap");
|
||||
for(i=0; i<me->pv->totvert; ++i) {
|
||||
old_map[i]= me->pv->vert_map[i]<me->totvert?0:1;
|
||||
}
|
||||
mesh_pmv_revert(ob, me);
|
||||
}
|
||||
|
||||
/* Kill sculpt data */
|
||||
active_ob= NULL;
|
||||
|
||||
/* Initalize map with which verts are to be hidden */
|
||||
me->pv->vert_map= MEM_mallocN(sizeof(unsigned)*me->totvert, "PMV vertmap");
|
||||
me->pv->totvert= me->totvert;
|
||||
me->totvert= 0;
|
||||
for(i=0; i<me->pv->totvert; ++i) {
|
||||
me->pv->vert_map[i]= mode ? HIDE:SHOW;
|
||||
for(j=0; j<4; ++j) {
|
||||
if(me->mvert[i].co[0] * plane_normals[j].x +
|
||||
me->mvert[i].co[1] * plane_normals[j].y +
|
||||
me->mvert[i].co[2] * plane_normals[j].z < plane_ds[j] ) {
|
||||
me->pv->vert_map[i]= mode ? SHOW:HIDE; /* Vert is outside the hide box */
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(old_map && old_map[i]) me->pv->vert_map[i]= 1;
|
||||
if(!me->pv->vert_map[i]) ++me->totvert;
|
||||
|
||||
}
|
||||
if(old_map) MEM_freeN(old_map);
|
||||
|
||||
/* Find out how many faces to show */
|
||||
for(i=0; i<me->totface; ++i) {
|
||||
if(!me->pv->vert_map[me->mface[i].v1] &&
|
||||
!me->pv->vert_map[me->mface[i].v2] &&
|
||||
!me->pv->vert_map[me->mface[i].v3]) {
|
||||
if(me->mface[i].v4) {
|
||||
if(!me->pv->vert_map[me->mface[i].v4])
|
||||
++face_cnt_show;
|
||||
}
|
||||
else ++face_cnt_show;
|
||||
}
|
||||
}
|
||||
/* Find out how many edges to show */
|
||||
for(i=0; i<me->totedge; ++i) {
|
||||
if(!me->pv->vert_map[me->medge[i].v1] &&
|
||||
!me->pv->vert_map[me->medge[i].v2])
|
||||
++edge_cnt_show;
|
||||
}
|
||||
|
||||
/* Create new vert array and reset each vert's map with map[old]=new index */
|
||||
nve= MEM_mallocN(sizeof(MVert)*me->pv->totvert, "PMV verts");
|
||||
ndx_show= 0; ndx_hide= me->totvert;
|
||||
for(i=0; i<me->pv->totvert; ++i) {
|
||||
if(me->pv->vert_map[i]) {
|
||||
me->pv->vert_map[i]= ndx_hide;
|
||||
nve[me->pv->vert_map[i]]= me->mvert[i];
|
||||
++ndx_hide;
|
||||
} else {
|
||||
me->pv->vert_map[i]= ndx_show;
|
||||
nve[me->pv->vert_map[i]]= me->mvert[i];
|
||||
++ndx_show;
|
||||
}
|
||||
}
|
||||
CustomData_free_layer_active(&me->vdata, CD_MVERT, me->pv->totvert);
|
||||
me->mvert= CustomData_add_layer(&me->vdata, CD_MVERT, CD_ASSIGN, nve, me->totvert);
|
||||
|
||||
/* Create new face array */
|
||||
me->pv->old_faces= me->mface;
|
||||
me->pv->totface= me->totface;
|
||||
me->mface= MEM_mallocN(sizeof(MFace)*face_cnt_show, "PMV faces");
|
||||
for(i=0; i<me->totface; ++i) {
|
||||
MFace *pr_f= &me->pv->old_faces[i];
|
||||
char show= 0;
|
||||
|
||||
if(me->pv->vert_map[pr_f->v1] < me->totvert &&
|
||||
me->pv->vert_map[pr_f->v2] < me->totvert &&
|
||||
me->pv->vert_map[pr_f->v3] < me->totvert) {
|
||||
if(pr_f->v4) {
|
||||
if(me->pv->vert_map[pr_f->v4] < me->totvert)
|
||||
show= 1;
|
||||
}
|
||||
else show= 1;
|
||||
}
|
||||
|
||||
if(show) {
|
||||
MFace *cr_f= &me->mface[face_ndx_show];
|
||||
*cr_f= *pr_f;
|
||||
cr_f->v1= me->pv->vert_map[pr_f->v1];
|
||||
cr_f->v2= me->pv->vert_map[pr_f->v2];
|
||||
cr_f->v3= me->pv->vert_map[pr_f->v3];
|
||||
cr_f->v4= pr_f->v4 ? me->pv->vert_map[pr_f->v4] : 0;
|
||||
test_index_face(cr_f,NULL,0,pr_f->v4?4:3);
|
||||
++face_ndx_show;
|
||||
}
|
||||
}
|
||||
me->totface= face_cnt_show;
|
||||
CustomData_set_layer(&me->fdata, CD_MFACE, me->mface);
|
||||
|
||||
/* Create new edge array */
|
||||
me->pv->old_edges= me->medge;
|
||||
me->pv->totedge= me->totedge;
|
||||
me->medge= MEM_mallocN(sizeof(MEdge)*edge_cnt_show, "PMV edges");
|
||||
me->pv->edge_map= MEM_mallocN(sizeof(int)*me->pv->totedge,"PMV edgemap");
|
||||
for(i=0; i<me->totedge; ++i) {
|
||||
if(me->pv->vert_map[me->pv->old_edges[i].v1] < me->totvert &&
|
||||
me->pv->vert_map[me->pv->old_edges[i].v2] < me->totvert) {
|
||||
MEdge *cr_e= &me->medge[edge_ndx_show];
|
||||
me->pv->edge_map[i]= edge_ndx_show;
|
||||
*cr_e= me->pv->old_edges[i];
|
||||
cr_e->v1= me->pv->vert_map[me->pv->old_edges[i].v1];
|
||||
cr_e->v2= me->pv->vert_map[me->pv->old_edges[i].v2];
|
||||
++edge_ndx_show;
|
||||
}
|
||||
else me->pv->edge_map[i]= -1;
|
||||
}
|
||||
me->totedge= edge_cnt_show;
|
||||
CustomData_set_layer(&me->edata, CD_MEDGE, me->medge);
|
||||
|
||||
/* XXX: DAG_object_flush_update(G.scene, OBACT, OB_RECALC_DATA); */
|
||||
}
|
||||
|
||||
static rcti sculptmode_pmv_box()
|
||||
{
|
||||
/*XXX: short down[2], mouse[2];
|
||||
rcti ret;
|
||||
|
||||
getmouseco_areawin(down);
|
||||
|
||||
while((get_mbut()&L_MOUSE) || (get_mbut()&R_MOUSE)) {
|
||||
getmouseco_areawin(mouse);
|
||||
|
||||
scrarea_do_windraw(curarea);
|
||||
|
||||
persp(PERSP_WIN);
|
||||
glLineWidth(2);
|
||||
setlinestyle(2);
|
||||
sdrawXORline(down[0],down[1],mouse[0],down[1]);
|
||||
sdrawXORline(mouse[0],down[1],mouse[0],mouse[1]);
|
||||
sdrawXORline(mouse[0],mouse[1],down[0],mouse[1]);
|
||||
sdrawXORline(down[0],mouse[1],down[0],down[1]);
|
||||
setlinestyle(0);
|
||||
glLineWidth(1);
|
||||
persp(PERSP_VIEW);
|
||||
|
||||
screen_swapbuffers();
|
||||
backdrawview3d(0);
|
||||
}
|
||||
|
||||
ret.xmin= down[0]<mouse[0]?down[0]:mouse[0];
|
||||
ret.ymin= down[1]<mouse[1]?down[1]:mouse[1];
|
||||
ret.xmax= down[0]>mouse[0]?down[0]:mouse[0];
|
||||
ret.ymax= down[1]>mouse[1]?down[1]:mouse[1];
|
||||
return ret;*/
|
||||
}
|
||||
|
||||
void sculptmode_pmv(int mode)
|
||||
{
|
||||
Object *ob= NULL; /*XXX: OBACT; */
|
||||
rcti hb_2d;
|
||||
|
||||
if(ob_get_key(ob)) {
|
||||
error("Cannot hide mesh with shape keys enabled");
|
||||
return;
|
||||
}
|
||||
|
||||
hb_2d= sculptmode_pmv_box(); /* Get 2D hide box */
|
||||
|
||||
sculptmode_correct_state();
|
||||
|
||||
waitcursor(1);
|
||||
|
||||
if(hb_2d.xmax-hb_2d.xmin > 3 && hb_2d.ymax-hb_2d.ymin > 3) {
|
||||
init_sculptmatrices();
|
||||
|
||||
sculptmode_do_pmv(ob,&hb_2d,mode);
|
||||
}
|
||||
else mesh_pmv_off(ob, get_mesh(ob));
|
||||
|
||||
/*XXX: scrarea_do_windraw(curarea); */
|
||||
|
||||
BIF_undo_push("Partial mesh hide");
|
||||
|
||||
waitcursor(0);
|
||||
}
|
||||
87
source/blender/editors/sculpt/sculpt_intern.h
Normal file
87
source/blender/editors/sculpt/sculpt_intern.h
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* $Id: BDR_sculptmode.h 13396 2008-01-25 04:17:38Z nicholasbishop $
|
||||
*
|
||||
* ***** 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.
|
||||
*
|
||||
* The Original Code is Copyright (C) 2006 by Nicholas Bishop
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): none yet.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
#ifndef BDR_SCULPTMODE_H
|
||||
#define BDR_SCULPTMODE_H
|
||||
|
||||
#include "DNA_listBase.h"
|
||||
#include "DNA_vec_types.h"
|
||||
#include "BKE_sculpt.h"
|
||||
|
||||
struct uiBlock;
|
||||
struct BrushAction;
|
||||
struct BrushData;
|
||||
struct IndexNode;
|
||||
struct KeyBlock;
|
||||
struct Mesh;
|
||||
struct Object;
|
||||
struct PartialVisibility;
|
||||
struct Scene;
|
||||
struct ScrArea;
|
||||
struct SculptData;
|
||||
struct SculptStroke;
|
||||
|
||||
struct SculptSession *sculpt_session(void);
|
||||
struct SculptData *sculpt_data(void);
|
||||
|
||||
/* Memory */
|
||||
void sculptmode_correct_state(void);
|
||||
|
||||
/* Interface */
|
||||
void sculptmode_draw_interface_tools(struct uiBlock *block,unsigned short cx, unsigned short cy);
|
||||
void sculptmode_draw_interface_brush(struct uiBlock *block,unsigned short cx, unsigned short cy);
|
||||
void sculptmode_draw_interface_textures(struct uiBlock *block,unsigned short cx, unsigned short cy);
|
||||
void sculptmode_rem_tex(void*,void*);
|
||||
void sculptmode_selectbrush_menu(void);
|
||||
void sculptmode_draw_mesh(int);
|
||||
void sculpt_paint_brush(char clear);
|
||||
void sculpt_stroke_draw();
|
||||
void sculpt_radialcontrol_start(int mode);
|
||||
|
||||
struct BrushData *sculptmode_brush(void);
|
||||
void do_symmetrical_brush_actions(struct BrushAction *a, short *, short *);
|
||||
|
||||
void sculptmode_update_tex(void);
|
||||
char sculpt_modifiers_active(struct Object *ob);
|
||||
void sculpt(void);
|
||||
void set_sculptmode(void);
|
||||
|
||||
/* Stroke */
|
||||
void sculpt_stroke_new(const int max);
|
||||
void sculpt_stroke_free();
|
||||
void sculpt_stroke_add_point(const short x, const short y);
|
||||
void sculpt_stroke_apply(struct BrushAction *);
|
||||
void sculpt_stroke_apply_all(struct BrushAction *);
|
||||
void sculpt_stroke_draw();
|
||||
|
||||
|
||||
/* Partial Mesh Visibility */
|
||||
void sculptmode_pmv(int mode);
|
||||
|
||||
#endif
|
||||
284
source/blender/editors/sculpt/stroke.c
Normal file
284
source/blender/editors/sculpt/stroke.c
Normal file
@@ -0,0 +1,284 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* ***** 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.
|
||||
*
|
||||
* The Original Code is Copyright (C) 2007 by Nicholas Bishop
|
||||
* All rights reserved.
|
||||
*
|
||||
* The Original Code is: all of this file.
|
||||
*
|
||||
* Contributor(s): none yet.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*
|
||||
* Storage and manipulation of sculptmode brush strokes.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "DNA_listBase.h"
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "BKE_sculpt.h"
|
||||
#include "BLI_blenlib.h"
|
||||
#include "BIF_gl.h"
|
||||
|
||||
#include "sculpt_intern.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
/* Temporary storage of input stroke control points */
|
||||
typedef struct StrokePoint {
|
||||
struct StrokePoint *next, *prev;
|
||||
short x, y;
|
||||
} StrokePoint;
|
||||
typedef struct SculptStroke {
|
||||
short (*loc)[2];
|
||||
int max;
|
||||
int index;
|
||||
float length;
|
||||
ListBase final;
|
||||
StrokePoint *final_mem;
|
||||
float offset;
|
||||
} SculptStroke;
|
||||
|
||||
void sculpt_stroke_new(const int max)
|
||||
{
|
||||
SculptSession *ss = sculpt_session();
|
||||
|
||||
ss->stroke = MEM_callocN(sizeof(SculptStroke), "SculptStroke");
|
||||
ss->stroke->loc = MEM_callocN(sizeof(short) * 4 * max, "SculptStroke.loc");
|
||||
ss->stroke->max = max;
|
||||
ss->stroke->index = -1;
|
||||
}
|
||||
|
||||
void sculpt_stroke_free()
|
||||
{
|
||||
SculptSession *ss = sculpt_session();
|
||||
if(ss && ss->stroke) {
|
||||
if(ss->stroke->loc) MEM_freeN(ss->stroke->loc);
|
||||
if(ss->stroke->final_mem) MEM_freeN(ss->stroke->final_mem);
|
||||
|
||||
MEM_freeN(ss->stroke);
|
||||
ss->stroke = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void sculpt_stroke_add_point(const short x, const short y)
|
||||
{
|
||||
SculptStroke *stroke = sculpt_session()->stroke;
|
||||
const int next = stroke->index + 1;
|
||||
|
||||
if(stroke->index == -1) {
|
||||
stroke->loc[0][0] = x;
|
||||
stroke->loc[0][1] = y;
|
||||
stroke->index = 0;
|
||||
}
|
||||
else if(next < stroke->max) {
|
||||
const int dx = x - stroke->loc[stroke->index][0];
|
||||
const int dy = y - stroke->loc[stroke->index][1];
|
||||
stroke->loc[next][0] = x;
|
||||
stroke->loc[next][1] = y;
|
||||
stroke->length += sqrt(dx*dx + dy*dy);
|
||||
stroke->index = next;
|
||||
}
|
||||
}
|
||||
|
||||
void sculpt_stroke_smooth(SculptStroke *stroke)
|
||||
{
|
||||
/* Apply smoothing (exclude the first and last points)*/
|
||||
StrokePoint *p = stroke->final.first;
|
||||
if(p && p->next && p->next->next) {
|
||||
for(p = p->next->next; p && p->next && p->next->next; p = p->next) {
|
||||
p->x = p->prev->prev->x*0.1 + p->prev->x*0.2 + p->x*0.4 + p->next->x*0.2 + p->next->next->x*0.1;
|
||||
p->y = p->prev->prev->y*0.1 + p->prev->y*0.2 + p->y*0.4 + p->next->y*0.2 + p->next->next->y*0.1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void sculpt_stroke_create_final()
|
||||
{
|
||||
SculptStroke *stroke = sculpt_session()->stroke;
|
||||
|
||||
if(stroke) {
|
||||
StrokePoint *p, *pnext;
|
||||
int i;
|
||||
|
||||
/* Copy loc into final */
|
||||
if(stroke->final_mem)
|
||||
MEM_freeN(stroke->final_mem);
|
||||
stroke->final_mem = MEM_callocN(sizeof(StrokePoint) * (stroke->index + 1) * 2, "SculptStroke.final");
|
||||
stroke->final.first = stroke->final.last = NULL;
|
||||
for(i = 0; i <= stroke->index; ++i) {
|
||||
p = &stroke->final_mem[i];
|
||||
p->x = stroke->loc[i][0];
|
||||
p->y = stroke->loc[i][1];
|
||||
BLI_addtail(&stroke->final, p);
|
||||
}
|
||||
|
||||
/* Remove shortest edges */
|
||||
if(stroke->final.first) {
|
||||
for(p = ((StrokePoint*)stroke->final.first)->next; p && p->next; p = pnext) {
|
||||
const int dx = p->x - p->prev->x;
|
||||
const int dy = p->y - p->prev->y;
|
||||
const float len = sqrt(dx*dx + dy*dy);
|
||||
pnext = p->next;
|
||||
if(len < 10) {
|
||||
BLI_remlink(&stroke->final, p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sculpt_stroke_smooth(stroke);
|
||||
|
||||
/* Subdivide edges */
|
||||
for(p = stroke->final.first; p && p->next; p = pnext) {
|
||||
StrokePoint *np = &stroke->final_mem[i++];
|
||||
|
||||
pnext = p->next;
|
||||
np->x = (p->x + p->next->x) / 2;
|
||||
np->y = (p->y + p->next->y) / 2;
|
||||
BLI_insertlink(&stroke->final, p, np);
|
||||
}
|
||||
|
||||
sculpt_stroke_smooth(stroke);
|
||||
}
|
||||
}
|
||||
|
||||
float sculpt_stroke_seglen(StrokePoint *p1, StrokePoint *p2)
|
||||
{
|
||||
int dx = p2->x - p1->x;
|
||||
int dy = p2->y - p1->y;
|
||||
return sqrt(dx*dx + dy*dy);
|
||||
}
|
||||
|
||||
float sculpt_stroke_final_length(SculptStroke *stroke)
|
||||
{
|
||||
StrokePoint *p;
|
||||
float len = 0;
|
||||
for(p = stroke->final.first; p && p->next; ++p)
|
||||
len += sculpt_stroke_seglen(p, p->next);
|
||||
return len;
|
||||
}
|
||||
|
||||
/* If partial is nonzero, cuts off apply after that length has been processed */
|
||||
static StrokePoint *sculpt_stroke_apply_generic(SculptStroke *stroke, struct BrushAction *a, const int partial)
|
||||
{
|
||||
const int sdspace = sculpt_data()->spacing;
|
||||
const short spacing = sdspace > 0 ? sdspace : 2;
|
||||
const int dots = sculpt_stroke_final_length(stroke) / spacing;
|
||||
int i;
|
||||
StrokePoint *p = stroke->final.first;
|
||||
float startloc = stroke->offset;
|
||||
|
||||
for(i = 0; i < dots && p && p->next; ++i) {
|
||||
const float dotloc = spacing * i;
|
||||
short co[2];
|
||||
float len = sculpt_stroke_seglen(p, p->next);
|
||||
float u, v;
|
||||
|
||||
/* Find edge containing dot */
|
||||
while(dotloc > startloc + len && p && p->next && p->next->next) {
|
||||
p = p->next;
|
||||
startloc += len;
|
||||
len = sculpt_stroke_seglen(p, p->next);
|
||||
}
|
||||
|
||||
if(!p || !p->next || dotloc > startloc + len)
|
||||
break;
|
||||
|
||||
if(partial && startloc > partial) {
|
||||
/* Calculate offset for next stroke segment */
|
||||
stroke->offset = startloc + len - dotloc;
|
||||
break;
|
||||
}
|
||||
|
||||
u = (dotloc - startloc) / len;
|
||||
v = 1 - u;
|
||||
|
||||
co[0] = p->x*v + p->next->x*u;
|
||||
co[1] = p->y*v + p->next->y*u;
|
||||
|
||||
do_symmetrical_brush_actions(a, co, NULL);
|
||||
}
|
||||
|
||||
return p ? p->next : NULL;
|
||||
}
|
||||
|
||||
void sculpt_stroke_apply(struct BrushAction *a)
|
||||
{
|
||||
SculptStroke *stroke = sculpt_session()->stroke;
|
||||
/* TODO: make these values user-modifiable? */
|
||||
const int partial_len = 100;
|
||||
const int min_len = 200;
|
||||
|
||||
if(stroke) {
|
||||
sculpt_stroke_create_final();
|
||||
|
||||
if(sculpt_stroke_final_length(stroke) > min_len) {
|
||||
StrokePoint *p = sculpt_stroke_apply_generic(stroke, a, partial_len);
|
||||
|
||||
/* Replace remaining values in stroke->loc with remaining stroke->final values */
|
||||
stroke->index = -1;
|
||||
stroke->length = 0;
|
||||
for(; p; p = p->next) {
|
||||
++stroke->index;
|
||||
stroke->loc[stroke->index][0] = p->x;
|
||||
stroke->loc[stroke->index][1] = p->y;
|
||||
if(p->next) {
|
||||
stroke->length += sculpt_stroke_seglen(p, p->next);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sculpt_stroke_apply_all(struct BrushAction *a)
|
||||
{
|
||||
SculptStroke *stroke = sculpt_session()->stroke;
|
||||
|
||||
sculpt_stroke_create_final();
|
||||
|
||||
if(stroke) {
|
||||
sculpt_stroke_apply_generic(stroke, a, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void sculpt_stroke_draw()
|
||||
{
|
||||
SculptStroke *stroke = sculpt_session()->stroke;
|
||||
|
||||
if(stroke) {
|
||||
StrokePoint *p;
|
||||
|
||||
/* Draws the original stroke */
|
||||
/*glColor3f(1, 0, 0);
|
||||
glBegin(GL_LINE_STRIP);
|
||||
for(i = 0; i <= stroke->index; ++i)
|
||||
glVertex2s(stroke->loc[i][0], stroke->loc[i][1]);
|
||||
glEnd();*/
|
||||
|
||||
/* Draws the smoothed stroke */
|
||||
glColor3f(0, 1, 0);
|
||||
glBegin(GL_LINE_STRIP);
|
||||
for(p = stroke->final.first; p; p = p->next)
|
||||
glVertex2s(p->x, p->y);
|
||||
glEnd();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user