*/}}
浏览代码

Smudge lifting and other small improvements

YimingWu 1 周之前
父节点
当前提交
c88d1eb4c7
共有 5 个文件被更改,包括 72 次插入24 次删除
  1. 35 16
      ournodes.c
  2. 23 6
      ouroperations.c
  3. 8 2
      ourpaint.h
  4. 2 0
      ourshader.cpp
  5. 4 0
      ourtranslations_zh-hans.c

+ 35 - 16
ournodes.c

@@ -51,12 +51,14 @@ void IDN_BrushSettingsInit(OurBrushSettingsNode* n, int NoCreate){
     if(!n->Force) n->Force=laCreateOutSocket(n,"FORCE",LA_PROP_FLOAT);
     if(!n->Accumulation) n->Accumulation=laCreateOutSocket(n,"ACCUM",LA_PROP_FLOAT);
     if(!n->DepletionSpeed) n->DepletionSpeed=laCreateOutSocket(n,"DRAIN",LA_PROP_FLOAT);
+    if(!n->SmudgeLifting) n->SmudgeLifting=laCreateOutSocket(n,"LIFT",LA_PROP_FLOAT);
     n->CanvasScale->Data=&n->rCanvasScale;
     n->Size->Data=&n->rSize;
     n->Transparency->Data=&n->rTransparency;
     n->Hardness->Data=&n->rHardness;
     n->Smudge->Data=&n->rSmudge;
     n->SmudgeLength->Data=&n->rSmudgeLength;
+    n->SmudgeLifting->Data=&n->rSmudgeLifting;
     n->DabsPerSize->Data=&n->rDabsPerSize;
     n->Slender->Data=&n->rSlender;
     n->Angle->Data=&n->rAngle;
@@ -74,6 +76,7 @@ void IDN_BrushSettingsDestroy(OurBrushSettingsNode* n){
     laDestroyOutSocket(n->SmudgeLength); laDestroyOutSocket(n->DabsPerSize); laDestroyOutSocket(n->Slender); laDestroyOutSocket(n->Angle);
     laDestroyOutSocket(n->CanvasScale); laDestroyOutSocket(n->Iteration); laDestroyOutSocket(n->Custom1); laDestroyOutSocket(n->Custom2);
     laDestroyOutSocket(n->Gunkyness); laDestroyOutSocket(n->Force); laDestroyOutSocket(n->Accumulation);  laDestroyOutSocket(n->DepletionSpeed);
+    laDestroyOutSocket(n->SmudgeLifting);
     strSafeDestroy(&n->Base.Name);
 }
 int IDN_BrushSettingsVisit(OurBrushSettingsNode* n, laNodeVisitInfo* vi){
@@ -88,6 +91,7 @@ int IDN_BrushSettingsEval(OurBrushSettingsNode* n){
     n->rHardness = Our->CurrentBrush->Hardness;
     n->rSmudge = Our->CurrentBrush->Smudge;
     n->rSmudgeLength = Our->CurrentBrush->SmudgeResampleLength;
+    n->rSmudgeLifting = Our->CurrentBrush->SmudgeLifting;
     n->rDabsPerSize = Our->CurrentBrush->DabsPerSize;
     n->rSlender = Our->CurrentBrush->Slender;
     n->rAngle = Our->CurrentBrush->Angle;
@@ -110,7 +114,7 @@ void IDN_BrushSettingsCopy(OurBrushSettingsNode* new, OurBrushSettingsNode* old,
     LA_IDN_OLD_DUPL(Smudge)      LA_IDN_OLD_DUPL(SmudgeLength)
     LA_IDN_OLD_DUPL(Transparency)LA_IDN_OLD_DUPL(Gunkyness)
     LA_IDN_OLD_DUPL(Force)       LA_IDN_OLD_DUPL(Accumulation)
-    LA_IDN_OLD_DUPL(DepletionSpeed)
+    LA_IDN_OLD_DUPL(DepletionSpeed) LA_IDN_OLD_DUPL(SmudgeLifting)
 }
 void ui_BrushSettingsNode(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
     laColumn* c=laFirstColumn(uil); OurBrushSettingsNode*n=This->EndInstance;
@@ -130,6 +134,7 @@ void ui_BrushSettingsNode(laUiList *uil, laPropPack *This, laPropPack *Extra, la
         laShowSeparator(uil,c)->Expand=1;
         laShowNodeSocket(uil,c,This,"smudge_length",0)->Flags|=LA_UI_SOCKET_LABEL_W;
         laShowNodeSocket(uil,c,This,"smudge",0)->Flags|=LA_UI_SOCKET_LABEL_W;
+        laShowNodeSocket(uil,c,This,"smudge_lifting",0)->Flags|=LA_UI_SOCKET_LABEL_W;
     laEndRow(uil,b);
     b=laBeginRow(uil,c,0,0);
         laShowSeparator(uil,c)->Expand=1;
@@ -169,6 +174,7 @@ void IDN_BrushOutputsInit(OurBrushOutputsNode* n, int NoCreate){
     if(!n->Force) n->Force=laCreateInSocket("FORCE",LA_PROP_FLOAT);
     if(!n->Accumulation) n->Accumulation=laCreateInSocket("ACCUM",LA_PROP_FLOAT);
     if(!n->DepletionSpeed) n->DepletionSpeed=laCreateInSocket("DRAIN",LA_PROP_FLOAT);
+    if(!n->SmudgeLifting) n->SmudgeLifting=laCreateInSocket("LIFTING",LA_PROP_FLOAT);
     strSafeSet(&n->Base.Name, "Brush Outputs");
 }
 void IDN_BrushOutputsDestroy(OurBrushOutputsNode* n){
@@ -177,18 +183,19 @@ void IDN_BrushOutputsDestroy(OurBrushOutputsNode* n){
     laDestroyInSocket(n->SmudgeLength); laDestroyInSocket(n->DabsPerSize); laDestroyInSocket(n->Slender); laDestroyInSocket(n->Angle);
     laDestroyInSocket(n->Color); laDestroyInSocket(n->Repeats); laDestroyInSocket(n->Discard);
     laDestroyInSocket(n->Gunkyness); laDestroyInSocket(n->Force); laDestroyInSocket(n->Accumulation); laDestroyInSocket(n->DepletionSpeed);
+    laDestroyInSocket(n->SmudgeLifting);
     strSafeDestroy(&n->Base.Name);
 }
 int IDN_BrushOutputsVisit(OurBrushOutputsNode* n, laNodeVisitInfo* vi){
     LA_GUARD_THIS_NODE(n,vi);
 #define BRUSH_OUT_VISIT(a) \
     if(LA_SRC_AND_PARENT(n->a)){ laBaseNode*bn=n->a->Source->Parent; LA_VISIT_NODE(bn,vi); }
-    BRUSH_OUT_VISIT(Offset) BRUSH_OUT_VISIT(Size) BRUSH_OUT_VISIT(Transparency)
-    BRUSH_OUT_VISIT(Hardness) BRUSH_OUT_VISIT(Smudge) BRUSH_OUT_VISIT(SmudgeLength)
-    BRUSH_OUT_VISIT(DabsPerSize) BRUSH_OUT_VISIT(Slender) BRUSH_OUT_VISIT(Angle)
-    BRUSH_OUT_VISIT(Color) BRUSH_OUT_VISIT(Repeats) BRUSH_OUT_VISIT(Discard)
-    BRUSH_OUT_VISIT(Gunkyness) BRUSH_OUT_VISIT(Force) BRUSH_OUT_VISIT(Accumulation)
-    BRUSH_OUT_VISIT(DepletionSpeed)
+    BRUSH_OUT_VISIT(Offset)         BRUSH_OUT_VISIT(Size)    BRUSH_OUT_VISIT(Transparency)
+    BRUSH_OUT_VISIT(Hardness)       BRUSH_OUT_VISIT(Smudge)  BRUSH_OUT_VISIT(SmudgeLength)
+    BRUSH_OUT_VISIT(DabsPerSize)    BRUSH_OUT_VISIT(Slender) BRUSH_OUT_VISIT(Angle)
+    BRUSH_OUT_VISIT(Color)          BRUSH_OUT_VISIT(Repeats) BRUSH_OUT_VISIT(Discard)
+    BRUSH_OUT_VISIT(Gunkyness)      BRUSH_OUT_VISIT(Force)   BRUSH_OUT_VISIT(Accumulation)
+    BRUSH_OUT_VISIT(DepletionSpeed) BRUSH_OUT_VISIT(SmudgeLifting)
 #undef BRUSH_OUT_VISIT
     LA_ADD_THIS_NODE(n,vi);
     return LA_DAG_FLAG_PERM;
@@ -223,15 +230,17 @@ int IDN_BrushOutputsEval(OurBrushOutputsNode* n){
     BRUSH_OUT_EVAL(DepletionSpeed)
     BRUSH_OUT_EVAL(Repeats)
     BRUSH_OUT_EVAL(Discard)
+    BRUSH_OUT_EVAL(SmudgeLifting)
 #undef BRUSH_OUT_EVAL
     return 1;
 }
 void IDN_BrushOutputsCopy(OurBrushOutputsNode* new, OurBrushOutputsNode* old, int DoRematch){
     if(DoRematch){
-        LA_IDN_NEW_LINK(Offset) LA_IDN_NEW_LINK(Size) LA_IDN_NEW_LINK(Transparency) LA_IDN_NEW_LINK(Hardness)
-        LA_IDN_NEW_LINK(Smudge) LA_IDN_NEW_LINK(SmudgeLength) LA_IDN_NEW_LINK(DabsPerSize) LA_IDN_NEW_LINK(Slender)
-        LA_IDN_NEW_LINK(Angle) LA_IDN_NEW_LINK(Color) LA_IDN_NEW_LINK(Repeats) LA_IDN_NEW_LINK(Discard)
-        LA_IDN_NEW_LINK(Gunkyness) LA_IDN_NEW_LINK(Force) LA_IDN_NEW_LINK(Accumulation) LA_IDN_NEW_LINK(DepletionSpeed)
+        LA_IDN_NEW_LINK(Offset)       LA_IDN_NEW_LINK(Size)         LA_IDN_NEW_LINK(Transparency) LA_IDN_NEW_LINK(Hardness)
+        LA_IDN_NEW_LINK(Smudge)       LA_IDN_NEW_LINK(SmudgeLength) LA_IDN_NEW_LINK(DabsPerSize)  LA_IDN_NEW_LINK(Slender)
+        LA_IDN_NEW_LINK(Angle)        LA_IDN_NEW_LINK(Color)        LA_IDN_NEW_LINK(Repeats)      LA_IDN_NEW_LINK(Discard)
+        LA_IDN_NEW_LINK(Gunkyness)    LA_IDN_NEW_LINK(Force)        LA_IDN_NEW_LINK(Accumulation) LA_IDN_NEW_LINK(DepletionSpeed)
+        LA_IDN_NEW_LINK(SmudgeLifting)
         return;
     }
     return;
@@ -253,6 +262,7 @@ void ui_BrushOutputsNode(laUiList *uil, laPropPack *This, laPropPack *Extra, laC
     b=laBeginRow(uil,c,0,0);
         laShowNodeSocket(uil,c,This,"smudge",0)->Flags|=LA_UI_SOCKET_LABEL_E;
         laShowNodeSocket(uil,c,This,"smudge_length",0)->Flags|=LA_UI_SOCKET_LABEL_E;
+        laShowNodeSocket(uil,c,This,"smudge_lifting",0)->Flags|=LA_UI_SOCKET_LABEL_E;
     laEndRow(uil,b);
     b=laBeginRow(uil,c,0,0);
         laShowNodeSocket(uil,c,This,"gunkyness",0)->Flags|=LA_UI_SOCKET_LABEL_E;
@@ -280,6 +290,7 @@ void IDN_BrushDeviceInit(OurBrushDeviceNode* n, int NoCreate){
         strSafeSet(&n->Base.Name, "Brush Device");
     }
     if(!n->Twist){ n->Twist=laCreateOutSocket(n,"TWIST",LA_PROP_FLOAT); }
+    if(!n->PigmentLoading){ n->PigmentLoading=laCreateOutSocket(n,"LOAD",LA_PROP_FLOAT); }
     n->Pressure->Data=&n->rPressure;
     n->Tilt->Data=n->rTilt; n->Tilt->ArrLen=2;
     n->Twist->Data=&n->rTwist;
@@ -289,10 +300,11 @@ void IDN_BrushDeviceInit(OurBrushDeviceNode* n, int NoCreate){
     n->Angle->Data=&n->rAngle;
     n->Length->Data=&n->rLength;
     n->LengthAccum->Data=&n->rLengthAccum;
+    n->PigmentLoading->Data=&n->rPigmentLoading;
 }
 void IDN_BrushDeviceDestroy(OurBrushDeviceNode* n){
     laDestroyOutSocket(n->Pressure); laDestroyOutSocket(n->Tilt); laDestroyOutSocket(n->Position); laDestroyOutSocket(n->IsEraser); laDestroyOutSocket(n->Speed);
-    laDestroyOutSocket(n->Angle); laDestroyOutSocket(n->Length); laDestroyOutSocket(n->LengthAccum); laDestroyOutSocket(n->Twist);
+    laDestroyOutSocket(n->Angle); laDestroyOutSocket(n->Length); laDestroyOutSocket(n->LengthAccum); laDestroyOutSocket(n->Twist); laDestroyOutSocket(n->PigmentLoading);
     strSafeDestroy(&n->Base.Name);
 }
 int IDN_BrushDeviceVisit(OurBrushDeviceNode* n, laNodeVisitInfo* vi){
@@ -310,13 +322,14 @@ int IDN_BrushDeviceEval(OurBrushDeviceNode* n){
     n->rSpeed = Our->CurrentBrush->EvalSpeed;
     n->rLength = Our->CurrentBrush->EvalStrokeLength;
     n->rLengthAccum = Our->CurrentBrush->EvalStrokeLengthAccum;
+    n->rPigmentLoading = Our->CurrentBrush->PigmentLoading;
     return 1;
 }
 void IDN_BrushDeviceCopy(OurBrushDeviceNode* new, OurBrushDeviceNode* old, int DoRematch){
     if(DoRematch){ return;}
-    LA_IDN_OLD_DUPL(Pressure) LA_IDN_OLD_DUPL(Tilt) LA_IDN_OLD_DUPL(Position) LA_IDN_OLD_DUPL(Twist)
-    LA_IDN_OLD_DUPL(IsEraser) LA_IDN_OLD_DUPL(Speed) LA_IDN_OLD_DUPL(Angle)
-    LA_IDN_OLD_DUPL(Length) LA_IDN_OLD_DUPL(LengthAccum)
+    LA_IDN_OLD_DUPL(Pressure) LA_IDN_OLD_DUPL(Tilt)        LA_IDN_OLD_DUPL(Position)   LA_IDN_OLD_DUPL(Twist)
+    LA_IDN_OLD_DUPL(IsEraser) LA_IDN_OLD_DUPL(Speed)       LA_IDN_OLD_DUPL(Angle)
+    LA_IDN_OLD_DUPL(Length)   LA_IDN_OLD_DUPL(LengthAccum) LA_IDN_OLD_DUPL(PigmentLoading)
 }
 void ui_BrushDeviceNode(laUiList *uil, laPropPack *This, laPropPack *Extra, laColumn *UNUSED, int context){
     laColumn* c=laFirstColumn(uil); OurBrushDeviceNode*n=This->EndInstance;
@@ -334,7 +347,10 @@ void ui_BrushDeviceNode(laUiList *uil, laPropPack *This, laPropPack *Extra, laCo
         laShowNodeSocket(uil,c,This,"is_eraser",0)->Flags|=LA_UI_SOCKET_LABEL_N;
     laEndRow(uil,b);
     b=laBeginRow(uil,c,0,0); laShowSeparator(uil,c)->Expand=1;
-        laShowNodeSocket(uil,c,This,"length_accum",0)->Flags|=LA_UI_SOCKET_LABEL_W; laShowNodeSocket(uil,c,This,"length",0)->Flags|=LA_UI_SOCKET_LABEL_W; laEndRow(uil,b);
+        laShowNodeSocket(uil,c,This,"length_accum",0)->Flags|=LA_UI_SOCKET_LABEL_W;
+        laShowNodeSocket(uil,c,This,"length",0)->Flags|=LA_UI_SOCKET_LABEL_W;
+        laShowNodeSocket(uil,c,This,"pigment_loading",0)->Flags|=LA_UI_SOCKET_LABEL_W;
+    laEndRow(uil,b);
     
 }
 
@@ -359,6 +375,7 @@ void ourRegisterNodes(){
     laAddSubGroup(pc,"hardness", "Hardness","Hardness","la_out_socket",0,0,0,offsetof(OurBrushSettingsNode,Hardness),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     laAddSubGroup(pc,"smudge", "Smudge","Smudge","la_out_socket",0,0,0,offsetof(OurBrushSettingsNode,Smudge),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     laAddSubGroup(pc,"smudge_length", "Smudge Length","Smudge length","la_out_socket",0,0,0,offsetof(OurBrushSettingsNode,SmudgeLength),0,0,0,0,0,0,0,LA_UDF_SINGLE);
+    laAddSubGroup(pc,"smudge_lifting", "Smudge Lifting","Smudge Lifting","la_out_socket",0,0,0,offsetof(OurBrushSettingsNode,SmudgeLifting),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     laAddSubGroup(pc,"dabs_per_size", "Dabs Per Size","Dabs per size","la_out_socket",0,0,0,offsetof(OurBrushSettingsNode,DabsPerSize),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     laAddSubGroup(pc,"slender", "Slender","Slender","la_out_socket",0,0,0,offsetof(OurBrushSettingsNode,Slender),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     laAddSubGroup(pc,"angle", "Angle","Angle","la_out_socket",0,0,0,offsetof(OurBrushSettingsNode,Angle),0,0,0,0,0,0,0,LA_UDF_SINGLE);
@@ -380,6 +397,7 @@ void ourRegisterNodes(){
     laAddSubGroup(pc,"hardness", "Hardness","Hardness","la_in_socket",0,0,0,offsetof(OurBrushOutputsNode,Hardness),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     laAddSubGroup(pc,"smudge", "Smudge","Smudge","la_in_socket",0,0,0,offsetof(OurBrushOutputsNode,Smudge),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     laAddSubGroup(pc,"smudge_length", "Smudge Length","Smudge length","la_in_socket",0,0,0,offsetof(OurBrushOutputsNode,SmudgeLength),0,0,0,0,0,0,0,LA_UDF_SINGLE);
+    laAddSubGroup(pc,"smudge_lifting", "Smudge Lifting","Smudge lifting","la_in_socket",0,0,0,offsetof(OurBrushOutputsNode,SmudgeLifting),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     laAddSubGroup(pc,"dabs_per_size", "Dabs Per Size","Dabs per size","la_in_socket",0,0,0,offsetof(OurBrushOutputsNode,DabsPerSize),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     laAddSubGroup(pc,"slender", "Slender","Slender","la_in_socket",0,0,0,offsetof(OurBrushOutputsNode,Slender),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     laAddSubGroup(pc,"angle", "Angle","Angle","la_in_socket",0,0,0,offsetof(OurBrushOutputsNode,Angle),0,0,0,0,0,0,0,LA_UDF_SINGLE);
@@ -403,6 +421,7 @@ void ourRegisterNodes(){
     laAddSubGroup(pc,"angle","Angle","Direction of the brush","la_out_socket",0,0,0,offsetof(OurBrushDeviceNode,Angle),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     laAddSubGroup(pc,"length","Length","Length of this brush stroke","la_out_socket",0,0,0,offsetof(OurBrushDeviceNode,Length),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     laAddSubGroup(pc,"length_accum","Accumulated Length","Accumulated stroke length","la_out_socket",0,0,0,offsetof(OurBrushDeviceNode,LengthAccum),0,0,0,0,0,0,0,LA_UDF_SINGLE);
+    laAddSubGroup(pc,"pigment_loading","Pigment Loading","Pigment loading of the brush","la_out_socket",0,0,0,offsetof(OurBrushDeviceNode,PigmentLoading),0,0,0,0,0,0,0,LA_UDF_SINGLE);
     
     LA_IDN_REGISTER("Brush Settings",U'⚙',OUR_IDN_BRUSH_SETTINGS,OUR_PC_IDN_BRUSH_SETTINGS, IDN_BrushSettings, OurBrushSettingsNode);
     LA_IDN_REGISTER("Brush Outputs",U'🌈',OUR_IDN_BRUSH_OUTPUTS,OUR_PC_IDN_BRUSH_OUTPUTS, IDN_BrushOutputs, OurBrushOutputsNode);

+ 23 - 6
ouroperations.c

@@ -529,6 +529,7 @@ void ourui_ToolsPanel(laUiList *uil, laPropPack *This, laPropPack *DetachedProps
             laShowItem(uil,c,&cb->PP,"dabs_per_size");
             OUR_BR laShowItem(uil,c,&cb->PP,"smudge")->Expand=1;  OUR_PRESSURE("pressure_smudge")  OUR_ER
             laShowItem(uil,c,&cb->PP,"smudge_resample_length");
+            laShowItem(uil,c,&cb->PP,"smudge_lifting");
             laShowItem(uil,c,&cb->PP,"gunkyness");
             OUR_BR laShowItem(uil,c,&cb->PP,"force")->Expand=1; OUR_PRESSURE("pressure_force") OUR_ER
             OUR_BR laShowItem(uil,c,&cb->PP,"depletion_speed")->Expand=1;  OUR_PRESSURE("pressure_depletion") OUR_ER
@@ -825,6 +826,11 @@ void ourui_OurPreference(laUiList *uil, laPropPack *This, laPropPack *DetachedPr
 
     laShowLabel(uil,c,"Undo:",0,0);
     laShowItem(uil,cr,0,"our.preferences.paint_undo_limit");
+    laUiItem* undoui=laShowItemFull(uil,cr,0,"our.preferences.tool_undo",0,"text=Tool Undo (Requre Restart)",0,0); undoui->Flags|=LA_UI_FLAGS_CHECKBOX;
+    laUiItem* bundo=laOnConditionThat(uil,cr,laPropExpression(&undoui->PP,""));{
+            laShowLabel(uil,cr,"When tool undo is enabled, brush nodes can't be moved across node racks (current technical limitation)\n"
+                            ,0,0)->Flags|=LA_TEXT_USE_NEWLINE|LA_TEXT_LINE_WRAP|LA_UI_FLAGS_DISABLED;
+    }laEndCondition(uil,bundo);
     
     laShowSeparator(uil,c);
 
@@ -1582,7 +1588,7 @@ int ourmod_PigmentLoader(laOperator* a, laEvent* e){
     if(e->type==LA_L_MOUSE_DOWN){ ui->State=LA_UI_ACTIVE; }
     if(ui->State==LA_UI_ACTIVE && (e->type&LA_MOUSE_EVENT)){
         if(e->type==LA_L_MOUSE_UP || e->type==LA_R_MOUSE_DOWN){ ui->State=LA_UI_NORMAL; laRedrawCurrentPanel(); return LA_RUNNING; }
-        real fac=(1.0f-OUR_MIXING_SPEED)*e->Pressure; b->PigmentLoading=1.0f-(1.0f-b->PigmentLoading)*fac; laNotifyUsersPP(&ui->PP);
+        real fac=(1.0f-OUR_MIXING_SPEED*e->Pressure); b->PigmentLoading=1.0f-(1.0f-b->PigmentLoading)*fac; laNotifyUsersPP(&ui->PP);
     }
 
     return LA_RUNNING;
@@ -1648,7 +1654,7 @@ int ourmod_PigmentMixer(laOperator* a, laEvent* e){
             our_PigmentToPreviewSelf(&Our->MixedPigment); laNotifyUsers("our.mixed_pigment"); }
         elif(es->On==4){ int d=e->y-es->LastY; int h=es->TargetVali+d/LA_RH; if(h<2){ h=2; } if(ui->Extent!=h){ ui->Extent=h; laRecalcCurrentPanel(); };  }
         elif(es->On==1 && e->type&LA_MOUSE_EVENT){ OurBrush* b=Our->CurrentBrush; if(!b){ return LA_RUNNING; }
-            real fac=(1.0f-OUR_MIXING_SPEED)*e->Pressure; b->PigmentLoading=1.0f-(1.0f-b->PigmentLoading)*fac; laNotifyUsers("our.tools.current_brush.pigment_loading");
+            real fac=(1.0f-OUR_MIXING_SPEED*e->Pressure); b->PigmentLoading=1.0f-(1.0f-b->PigmentLoading)*fac; laNotifyUsers("our.tools.current_brush.pigment_loading");
             laRedrawCurrentPanel(); es->TargetValf=e->Pressure;
         }
     }
@@ -2897,7 +2903,7 @@ int our_PaintGetDabs(OurBrush* b, OurLayer* l, real x, real y, real xto, real yt
     b->EvalSize=bsize; b->EvalHardness=b->Hardness; b->EvalSmudge=b->Smudge; b->EvalSmudgeLength=b->SmudgeResampleLength;
     b->EvalTransparency=b->Transparency; b->EvalDabsPerSize=b->DabsPerSize; b->EvalSlender=b->Slender; b->EvalAngle=b->Angle;
     b->EvalSpeed=tnsDistIdv2(x,y,xto,yto)/bsize; b->EvalForce=b->Force; b->EvalGunkyness=b->Gunkyness; b->EvalAccumulation=b->Accumulation;
-    b->EvalDepletionSpeed=b->DepletionSpeed; real drain_delta;
+    b->EvalSmudgeLifting=b->SmudgeLifting; b->EvalDepletionSpeed=b->DepletionSpeed; real drain_delta;
     if(Our->ResetBrush){ b->LastX=x; b->LastY=y; b->LastAngle=atan2(yto-y,xto-x); b->EvalStrokeLength=0; Our->ResetBrush=0; }
     real this_angle=atan2(yto-y,xto-x);
     if(b->LastAngle-this_angle>TNS_PI){ this_angle+=(TNS_PI*2); }
@@ -2927,7 +2933,7 @@ int our_PaintGetDabs(OurBrush* b, OurLayer* l, real x, real y, real xto, real yt
             if(b->Iteration==0){ drain_delta=b->EvalDepletionSpeed/b->EvalDabsPerSize/100.0f*pfac(b->PressureDepletion); }
     #undef pfac
     #undef pfacm
-            od->Gunkyness = b->EvalGunkyness; od->Slender = b->EvalSlender;
+            od->Gunkyness = b->EvalGunkyness; od->Slender = b->EvalSlender; od->SmudgeLifting=b->EvalSmudgeLifting;
             od->Angle=b->EvalAngle; if(b->TwistAngle){ od->Angle=tnsInterpolate(last_twist,Twist,r); }
             xmin=TNS_MIN2(xmin, od->X-od->Size); xmax=TNS_MAX2(xmax, od->X+od->Size); 
             ymin=TNS_MIN2(ymin, od->Y-od->Size); ymax=TNS_MAX2(ymax, od->Y+od->Size);
@@ -2979,6 +2985,7 @@ void our_PaintDoDab(OurDab* d, int tl, int tr, int tu, int tb){
     glUniform1f(OURU->uBrushSize,d->Size);
     glUniform1f(OURU->uBrushHardness,d->Hardness);
     glUniform1f(OURU->uBrushSmudge,d->Smudge);
+    glUniform1f(OURU->uBrushSmudgeLifting,d->SmudgeLifting+1);
     glUniform1f(OURU->uBrushSlender,d->Slender);
     glUniform1f(OURU->uBrushAngle,d->Angle);
     glUniform2fv(OURU->uBrushDirection,1,d->Direction);
@@ -3128,6 +3135,7 @@ void our_ReadWidgetColor(laCanvasExtra*e,int x,int y){
             pd.Reflectance[i]=pow(pigment[i]/255.0f,2.2f); pow(pd.Absorption[i]=pigment[i+8]/255.0f,2.2f);
             pd.Reflectance[i+8]=pow(pigment[i+16]/255.0f,2.2f); pow(pd.Absorption[i+8]=pigment[i+24]/255.0f,2.2f);
         }
+        pd.Reflectance[15]=(*((uint16_t*)&pigment[22]))/65535.0f; pd.Absorption[15]=(*((uint16_t*)&pigment[30]))/65535.0f;
         //printf("\nread:\n");
         //for(int i=0;i<16;i++) { printf("%.3f ",pd.Reflectance[i]); } printf("\n");
         //for(int i=0;i<16;i++) { printf("%.3f ",pd.Absorption[i]); } printf("\n");
@@ -4520,7 +4528,7 @@ void ourpost_Canvas(void* unused){
     laMarkMemClean(Our->CanvasSaverDummyList.pFirst);
 }
 void ourpost_Brush(OurBrush* brush){
-    if(brush->Version<50){ brush->Accumulation=3; brush->PressureAccumulation=1; }
+    if(brush->Version<50){ brush->Accumulation=3; brush->PressureAccumulation=1; if(brush->Smudge>0){ brush->SmudgeLifting=0.5; } }
 }
 
 
@@ -4983,6 +4991,9 @@ void ourRegisterEverything(){
     p=laAddEnumProperty(pc,"default_canvas_type","Default Canvas Type","Default canvas type",0,0,0,0,0,offsetof(OurPaint,DefaultCanvasType),0,0,0,0,0,0,0,0,0,0);
     laAddEnumItemAs(p,"RGBA","RGBA","Use RGBA Canvas as default",0,0);
     laAddEnumItemAs(p,"Pigment","Pigment","Use Pigment Canvas as default",1,0);
+    p=laAddEnumProperty(pc,"tool_undo","Tool Undo","Whether to enable undo for tools. Limitations apply. Effective upon restart.",LA_WIDGET_ENUM_HIGHLIGHT,0,0,0,0,offsetof(OurPaint,ToolUndo),0,0,0,0,0,0,0,0,0,0);
+    laAddEnumItemAs(p,"FALSE","No","Don't enable undo",0,0);
+    laAddEnumItemAs(p,"TRUE","Yes","Enable undo (Restrictions apply)",1,0);
     
     pc=laAddPropertyContainer("our_tools","Our Tools","OurPaint tools",0,0,sizeof(OurPaint),0,0,1);
     laPropContainerExtraFunctions(pc,0,0,0,ourpropagate_Tools,0);
@@ -5043,6 +5054,7 @@ void ourRegisterEverything(){
     laAddFloatProperty(pc,"transparency","Transparency","Transparency of a dab",0,0,0,1,0,0.05,0.5,0,offsetof(OurBrush,Transparency),0,0,0,0,0,0,0,0,0,0,0);
     laAddFloatProperty(pc,"hardness","Hardness","Hardness of the brush",0,0,0,1,0,0.05,0.95,0,offsetof(OurBrush,Hardness),0,0,0,0,0,0,0,0,0,0,0);
     laAddFloatProperty(pc,"smudge","Smudge","Smudge of the brush",0,0,0,1,0,0.05,0.95,0,offsetof(OurBrush,Smudge),0,0,0,0,0,0,0,0,0,0,0);
+    laAddFloatProperty(pc,"smudge_lifting","Smudge Lifting","How much paint does smudge brush lift",0,0,0,1,0,0.05,0.5,0,offsetof(OurBrush,SmudgeLifting),0,0,0,0,0,0,0,0,0,0,0);
     laAddFloatProperty(pc,"dabs_per_size","Dabs Per Size","How many dabs per size of the brush",0,0,0,0,0,0,0,0,offsetof(OurBrush,DabsPerSize),0,0,0,0,0,0,0,0,0,0,0);
     laAddFloatProperty(pc,"smudge_resample_length","Smudge Resample Length","How long of a distance (based on size) should elapse before resampling smudge",0,0,0,0,0,0,0,0,offsetof(OurBrush,SmudgeResampleLength),0,0,0,0,0,0,0,0,0,0,0);
     laAddFloatProperty(pc,"slender","Slender","Slenderness of the brush",0,0, 0,10,0,0.1,0,0,offsetof(OurBrush,Slender),0,0,0,0,0,0,0,0,0,0,0);
@@ -5388,6 +5400,7 @@ void ourGetUniforms(int CanvasProgram, int DisplayProgram, int CompositionProgra
     OURU->uBrushGunkyness=glGetUniformLocation(CanvasProgram,"uBrushGunkyness");
     OURU->uBrushErasing=glGetUniformLocation(CanvasProgram,"uBrushErasing");
     OURU->uBrushMix=glGetUniformLocation(CanvasProgram,"uBrushMix");
+    OURU->uBrushSmudgeLifting=glGetUniformLocation(CanvasProgram,"uBrushSmudgeLifting");
 
 #ifdef LA_USE_GLES
     OURU->uBrushRoutineSelectionES=glGetUniformLocation(CanvasProgram, "uBrushRoutineSelectionES");
@@ -5580,6 +5593,7 @@ int ourInit(){
 
     Our->FileRegistered = our_FileAssociationsRegistered();
 
+    Our->ToolUndo = 0;
     Our->SegmentedWrite = 1;
     Our->CanvasVersion=ourget_AssetVersion(0);
     Our->AlphaMode=1;
@@ -5626,7 +5640,10 @@ void ourFinalize(){
 
     laMarkMemClean(Our->CanvasSaverDummyList.pFirst);
 
-    laAddRootDBInst("our.tools");
+    if(Our->ToolUndo){
+        MAIN.InitArgs.DisableUnsupportedUndo=1;
+        laAddRootDBInst("our.tools");
+    }
     laAddRootDBInst("our.canvas");
 }
 

+ 8 - 2
ourpaint.h

@@ -190,6 +190,7 @@ STRUCTURE(OurBrushSettingsNode){
     laNodeOutSocket* Smudge;       real rSmudge;
     laNodeOutSocket* DabsPerSize;  real rDabsPerSize;
     laNodeOutSocket* SmudgeLength; real rSmudgeLength;
+    laNodeOutSocket* SmudgeLifting;real rSmudgeLifting;
     laNodeOutSocket* Slender;      real rSlender;
     laNodeOutSocket* Angle;        real rAngle;
     laNodeOutSocket* Gunkyness;    real rGunkyness;
@@ -210,6 +211,7 @@ STRUCTURE(OurBrushOutputsNode){
     laNodeInSocket* Smudge;
     laNodeInSocket* DabsPerSize;
     laNodeInSocket* SmudgeLength;
+    laNodeInSocket* SmudgeLifting;
     laNodeInSocket* Slender;
     laNodeInSocket* Angle;
     laNodeInSocket* Color;
@@ -231,6 +233,7 @@ STRUCTURE(OurBrushDeviceNode){
     laNodeOutSocket* Angle;    real rAngle;
     laNodeOutSocket* Length;   real rLength;
     laNodeOutSocket* LengthAccum; real rLengthAccum;
+    laNodeOutSocket* PigmentLoading; real rPigmentLoading;
 };
 
 STRUCTURE(OurBrush){
@@ -240,7 +243,7 @@ STRUCTURE(OurBrush){
     real DabsPerSize;
     real Hardness;
     real Transparency;
-    real Smudge;
+    real Smudge,SmudgeLifting;
     real SmudgeResampleLength; real SmudgeAccum; int SmudgeRestart; real BrushRemainingDist;
     real Slender;
     real Angle;
@@ -274,6 +277,7 @@ STRUCTURE(OurBrush){
     real EvalTransparency;
     real EvalSmudge;
     real EvalSmudgeLength;
+    real EvalSmudgeLifting;
     real EvalSlender;
     real EvalAngle;
     real EvalForce, EvalGunkyness;
@@ -297,7 +301,7 @@ STRUCTURE(OurDab){
     float X,Y;
     float Size;
     float Hardness;
-    float Smudge; int ResampleSmudge;
+    float Smudge, SmudgeLifting; int ResampleSmudge;
     float Color[4];
     float Slender;
     float Angle;
@@ -474,6 +478,7 @@ STRUCTURE(BrushUniforms){
     GLint uMixRoutineSelectionES;
     GLint uBrushErasing;
     GLint uBrushMix;
+    GLint uBrushSmudgeLifting;
     GLint RoutineDoDabs;
     GLint RoutineDoSample;
     GLint RoutineDoMixNormal;
@@ -557,6 +562,7 @@ STRUCTURE(OurPaint){
     int PigmentDisplayMethod;
     int PaletteInColorsPanel;
     int DefaultCanvasType;
+    int ToolUndo;
 
     tnsTexture* SmudgeTexture;
     GLuint CanvasShader;         GLuint CanvasProgram;

+ 2 - 0
ourshader.cpp

@@ -84,6 +84,7 @@ uniform vec4 uBrushColor;
 uniform vec4 uBackgroundColor;
 uniform int uBrushErasing;
 uniform int uBrushMix;
+uniform float uBrushSmudgeLifting;
 
 #ifdef OUR_GLES
 uniform int uBrushRoutineSelectionES;
@@ -468,6 +469,7 @@ void DoSample(){
     PigmentData color=PIGMENT_BLANK; for(int i=0;i<WORKGROUP_SIZE;i++){
         PigmentData dabc; GetImgPixel(smudge_buckets, ivec2(i*2+128,0), dabc); color=PigmentMix(color,dabc,1.0/(float(i)+1.));
     }
+    if(uBrushSmudgeLifting>1.0 && color.a[15]>0){ color.a[15]=pow(color.a[15],uBrushSmudgeLifting); }
     PigmentData oldcolor; GetImgPixel(smudge_buckets, ivec2(0,0), oldcolor);
     //PigmentMultiply(color,2./WORKGROUP_SIZE);
     WriteImgPixel(smudge_buckets,ivec2(2,0),uBrushErasing==2?color:oldcolor);

+ 4 - 0
ourtranslations_zh-hans.c

@@ -23,6 +23,10 @@ extern tnsMain* T;
 extern OurPaint *Our;
 
 static const char *entries[]={
+"When tool undo is enabled, brush nodes can't be moved across node racks (current technical limitation)",
+    "工具撤销启用时,笔刷节点不能在挂架上左右移动(目前的技术限制)",
+"Tool Undo (Requre Restart)","工具撤销(需要重启)",
+"Smudge Lifting","涂抹稀释",
 "Depletion Speed","耗尽速度",
 "Mixing Speed","混合速度",
 "Pigment Conversion Method:","以何种方式将颜料转换为像素:",