Page MenuHome

vcolbake_2.patch

File Metadata

Author
Alex Fraser (adfries)
Created
Nov 13 2013, 4:02 PM

vcolbake_2.patch

diff --git a/intern/tools/credits_svn_gen.py b/intern/tools/credits_svn_gen.py
index 8d160a9..dbcea99 100644
--- a/intern/tools/credits_svn_gen.py
+++ b/intern/tools/credits_svn_gen.py
@@ -118,7 +118,7 @@ contrib_companies = [
"<b>Unity Technologies</b> - FBX Exporter",
"<b>BioSkill GmbH</b> - H3D compatibility for X3D Exporter, "
"OBJ Nurbs Import/Export",
- "<b>AutoCRC</b> - Improvements to fluid particles",
+ "<b>AutoCRC</b> - Improvements to fluid particles, vertex color baking",
]
# ignore commits containing these messages
diff --git a/release/scripts/startup/bl_ui/properties_render.py b/release/scripts/startup/bl_ui/properties_render.py
index 815f0a7..3c643b8 100644
--- a/release/scripts/startup/bl_ui/properties_render.py
+++ b/release/scripts/startup/bl_ui/properties_render.py
@@ -630,9 +630,12 @@ class RENDER_PT_bake(RenderButtonsPanel, Panel):
split = layout.split()
col = split.column()
- col.prop(rd, "use_bake_clear")
- col.prop(rd, "bake_margin")
- col.prop(rd, "bake_quad_split", text="Split")
+ col.prop(rd, "use_bake_to_vertex_color")
+ sub = col.column()
+ sub.active = not rd.use_bake_to_vertex_color
+ sub.prop(rd, "use_bake_clear")
+ sub.prop(rd, "bake_margin")
+ sub.prop(rd, "bake_quad_split", text="Split")
col = split.column()
col.prop(rd, "use_bake_selected_to_active")
diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h
index de04e83..c8dda8c 100644
--- a/source/blender/makesdna/DNA_scene_types.h
+++ b/source/blender/makesdna/DNA_scene_types.h
@@ -1043,6 +1043,7 @@ typedef struct Scene {
#define R_BAKE_NORMALIZE 8
#define R_BAKE_MULTIRES 16
#define R_BAKE_LORES_MESH 32
+#define R_BAKE_VCOL 64
/* bake_normal_space */
#define R_BAKE_SPACE_CAMERA 0
diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c
index fc3e7d9..731130c 100644
--- a/source/blender/makesrna/intern/rna_scene.c
+++ b/source/blender/makesrna/intern/rna_scene.c
@@ -3082,6 +3082,11 @@ static void rna_def_scene_render_data(BlenderRNA *brna)
prop= RNA_def_property(srna, "use_bake_lores_mesh", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "bake_flag", R_BAKE_LORES_MESH);
RNA_def_property_ui_text(prop, "Low Resolution Mesh", "Calculate heights against unsubdivided low resolution mesh");
+
+ prop= RNA_def_property(srna, "use_bake_to_vertex_color", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "bake_flag", R_BAKE_VCOL);
+ RNA_def_property_ui_text(prop, "Bake to Vertex Colour",
+ "Bake to vertex colors instead of to a UV-mapped image.");
/* stamp */
diff --git a/source/blender/render/intern/include/renderdatabase.h b/source/blender/render/intern/include/renderdatabase.h
index ebbbfb0..61e4db5 100644
--- a/source/blender/render/intern/include/renderdatabase.h
+++ b/source/blender/render/intern/include/renderdatabase.h
@@ -67,6 +67,8 @@ typedef struct VlakTableNode {
struct MTFace *mtface;
struct MCol *mcol;
int totmtface, totmcol;
+ /* Index of face in source mesh. */
+ int *origindex;
float *surfnor;
float *tangent;
struct RadFace **radface;
@@ -116,6 +118,7 @@ float *RE_vertren_get_winspeed(struct ObjectInstanceRen *obi, struct VertRen *ve
struct MTFace *RE_vlakren_get_tface(struct ObjectRen *obr, VlakRen *ren, int n, char **name, int verify);
struct MCol *RE_vlakren_get_mcol(struct ObjectRen *obr, VlakRen *ren, int n, char **name, int verify);
+int *RE_vlakren_get_origindex(struct ObjectRen *obr, VlakRen *vlak, int verify);
float *RE_vlakren_get_surfnor(struct ObjectRen *obr, VlakRen *ren, int verify);
float *RE_vlakren_get_nmap_tangent(struct ObjectRen *obr, VlakRen *ren, int verify);
RadFace **RE_vlakren_get_radface(struct ObjectRen *obr, VlakRen *ren, int verify);
diff --git a/source/blender/render/intern/source/convertblender.c b/source/blender/render/intern/source/convertblender.c
index b18a87d..94a3c67 100644
--- a/source/blender/render/intern/source/convertblender.c
+++ b/source/blender/render/intern/source/convertblender.c
@@ -3248,7 +3248,8 @@ static void init_render_mesh(Render *re, ObjectRen *obr, int timeoffset)
CustomDataMask mask;
float xn, yn, zn, imat[3][3], mat[4][4]; //nor[3],
float *orco=0;
- int need_orco=0, need_stress=0, need_nmap_tangent=0, need_tangent=0;
+ int need_orco=0, need_stress=0, need_nmap_tangent=0, need_tangent=0,
+ need_origindex=0;
int a, a1, ok, vertofs;
int end, do_autosmooth=0, totvert = 0;
int use_original_normals= 0;
@@ -3297,7 +3298,11 @@ static void init_render_mesh(Render *re, ObjectRen *obr, int timeoffset)
}
need_nmap_tangent= 1;
}
-
+
+ /* origindex currently only used when baking to vertex colors */
+ if(re->flag & R_BAKING && re->r.bake_flag & R_BAKE_VCOL)
+ need_origindex= 1;
+
/* check autosmooth and displacement, we then have to skip only-verts optimize */
do_autosmooth |= (me->flag & ME_AUTOSMOOTH);
if(do_autosmooth)
@@ -3446,6 +3451,7 @@ static void init_render_mesh(Render *re, ObjectRen *obr, int timeoffset)
MTFace *mtface, *mtf;
MCol *mcol, *mc;
int index, mtfn= 0, mcn= 0, mtng=0, vindex;
+ int *origindex;
char *name;
int nr_verts = v4!=0 ? 4 : 3;
@@ -3483,6 +3489,11 @@ static void init_render_mesh(Render *re, ObjectRen *obr, int timeoffset)
}
}
}
+
+ if(need_origindex) {
+ origindex = RE_vlakren_get_origindex(obr, vlr, 1);
+ *origindex = a;
+ }
}
}
}
diff --git a/source/blender/render/intern/source/rendercore.c b/source/blender/render/intern/source/rendercore.c
index d2683f2..3ebc022 100644
--- a/source/blender/render/intern/source/rendercore.c
+++ b/source/blender/render/intern/source/rendercore.c
@@ -21,6 +21,9 @@
* Contributors: Hos, Robert Wenzlaff.
* Contributors: 2004/2005/2006 Blender Foundation, full recode
*
+ * Vertex color baking
+ * Copyright 2011 AutoCRC
+ *
* ***** END GPL LICENSE BLOCK *****
*/
@@ -51,9 +54,11 @@
#include "DNA_image_types.h"
#include "DNA_lamp_types.h"
#include "DNA_material_types.h"
+#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_group_types.h"
+#include "BKE_customdata.h"
#include "BKE_global.h"
#include "BKE_image.h"
#include "BKE_main.h"
@@ -1979,12 +1984,16 @@ typedef struct BakeShade {
ZSpan *zspan;
Image *ima;
ImBuf *ibuf;
-
+
+ /* vdone: number of faces baked */
int rectx, recty, quad, type, vdone, ready;
float dir[3];
Object *actob;
-
+
+ /* Output: vertex color or image data. If vcol is not NULL, rect and
+ rect_float should be NULL. */
+ MCol *vcol;
unsigned int *rect;
float *rect_float;
@@ -2156,7 +2165,8 @@ static void bake_shade(void *handle, Object *ob, ShadeInput *shi, int UNUSED(qua
}
}
- if(bs->rect_float) {
+ if(bs->rect_float && !bs->vcol) {
+ /* Target image is float (HDR). */
float *col= bs->rect_float + 4*(bs->rectx*y + x);
VECCOPY(col, shr.combined);
if (bs->type==RE_BAKE_ALL || bs->type==RE_BAKE_TEXTURE) {
@@ -2166,7 +2176,8 @@ static void bake_shade(void *handle, Object *ob, ShadeInput *shi, int UNUSED(qua
}
}
else {
- char *col= (char *)(bs->rect + bs->rectx*y + x);
+ /* Target is char (LDR). */
+ char col[4];
if (ELEM(bs->type, RE_BAKE_ALL, RE_BAKE_TEXTURE) && (R.r.color_mgt_flag & R_COLOR_MANAGEMENT)) {
float srgb[3];
@@ -2188,6 +2199,17 @@ static void bake_shade(void *handle, Object *ob, ShadeInput *shi, int UNUSED(qua
} else {
col[3]= 255;
}
+
+ if (bs->vcol) {
+ /* Vertex colour baking. Vcol has no useful alpha channel (it exists
+ but is used only for vertex painting). */
+ bs->vcol->b= col[0];
+ bs->vcol->g= col[1];
+ bs->vcol->r= col[2];
+ } else {
+ char *imcol= (char *)(bs->rect + bs->rectx*y + x);
+ QUATCOPY(imcol, col);
+ }
}
if (bs->rect_mask) {
@@ -2206,16 +2228,29 @@ static void bake_displacement(void *handle, ShadeInput *UNUSED(shi), float dist,
disp = 0.5f + dist; /* alter the range from [-0.5,0.5] to [0,1]*/
}
- if(bs->rect_float) {
+ if(bs->rect_float && !bs->vcol) {
+ /* Target is float (HDR). */
float *col= bs->rect_float + 4*(bs->rectx*y + x);
col[0] = col[1] = col[2] = disp;
col[3]= 1.0f;
} else {
- char *col= (char *)(bs->rect + bs->rectx*y + x);
+ /* Target is char (LDR). */
+ char col[4];
col[0]= FTOCHAR(disp);
col[1]= FTOCHAR(disp);
col[2]= FTOCHAR(disp);
col[3]= 255;
+
+ if(bs->vcol) {
+ /* Vertex colour baking. Vcol has no useful alpha channel (it exists
+ but is used only for vertex painting). */
+ bs->vcol->b= col[0];
+ bs->vcol->g= col[1];
+ bs->vcol->r= col[2];
+ } else {
+ char *imcol= (char *)(bs->rect + bs->rectx*y + x);
+ QUATCOPY(imcol, col);
+ }
}
if (bs->rect_mask) {
bs->rect_mask[bs->rectx*y + x] = FILTER_MASK_USED;
@@ -2433,13 +2468,47 @@ static int get_next_bake_face(BakeShade *bs)
vlr= RE_findOrAddVlak(obr, v);
if((bs->actob && bs->actob == obr->ob) || (!bs->actob && (obr->ob->flag & SELECT))) {
- tface= RE_vlakren_get_tface(obr, vlr, obr->bakemtface, NULL, 0);
- if(tface && tface->tpage) {
- Image *ima= tface->tpage;
- ImBuf *ibuf= BKE_image_get_ibuf(ima, NULL);
+ if(R.r.bake_flag & R_BAKE_VCOL) {
+ /* Gather face data for vertex colour bake */
+ Mesh *me;
+ int *origindex, vcollayer;
+ CustomDataLayer *cdl;
+
+ origindex = RE_vlakren_get_origindex(obr, vlr, 0);
+ if(origindex == NULL)
+ continue;
+
+ if(obr->ob->type != OB_MESH)
+ continue;
+ me = obr->ob->data;
+#if 0
+ /* Only shade selected faces. */
+ if((me->mface[*origindex].flag & ME_FACE_SEL) == 0)
+ continue;
+#endif
+
+ vcollayer = CustomData_get_render_layer_index(&me->fdata, CD_MCOL);
+ if(vcollayer == -1)
+ continue;
+
+ cdl = &me->fdata.layers[vcollayer];
+ bs->vcol = cdl->data;
+ bs->vcol += *origindex * sizeof(MCol);
+ }
+ else {
+ /* Get image for baking to texture */
+ Image *ima;
+ ImBuf *ibuf;
const float vec_alpha[4]= {0.0f, 0.0f, 0.0f, 0.0f};
const float vec_solid[4]= {0.0f, 0.0f, 0.0f, 1.0f};
+
+ tface= RE_vlakren_get_tface(obr, vlr, obr->bakemtface, NULL, 0);
+ if(!tface || !tface->tpage)
+ continue;
+
+ ima= tface->tpage;
+ ibuf= BKE_image_get_ibuf(ima, NULL);
if(ibuf==NULL)
continue;
@@ -2464,16 +2533,16 @@ static int get_next_bake_face(BakeShade *bs)
/* might be read by UI to set active image for display */
R.bakebuf= ima;
}
-
- bs->obi= obi;
- bs->vlr= vlr;
-
- bs->vdone++; /* only for error message if nothing was rendered */
- v++;
-
- BLI_unlock_thread(LOCK_CUSTOM1);
- return 1;
}
+
+ bs->obi= obi;
+ bs->vlr= vlr;
+
+ bs->vdone++; /* only for error message if nothing was rendered */
+ v++;
+
+ BLI_unlock_thread(LOCK_CUSTOM1);
+ return 1;
}
}
}
@@ -2482,6 +2551,69 @@ static int get_next_bake_face(BakeShade *bs)
return 0;
}
+/* Bake all vertices of a face. Actually, this still works on a face-by-face
+ basis, and each vertex on each face is shaded. Vertex colors are a property
+ of faces, not vertices. */
+static void shade_verts(BakeShade *bs)
+{
+ VlakRen *vlr= bs->vlr;
+ MCol *basevcol;
+ int i1, i2, i3, i4;
+
+ /* Disable baking to image; write to vcol instead (pointer set below). */
+ bs->ima= NULL;
+ bs->rect= NULL;
+ bs->rect_float= NULL;
+
+ bs->quad= 0;
+
+ /* No anti-aliasing for vertices. */
+ bs->dxco[0]= bs->dxco[1]= bs->dxco[2]= 0.0f;
+ bs->dyco[0]= bs->dyco[1]= bs->dyco[2]= 0.0f;
+
+ /* Shade each vertex of the face. u and v are barycentric coordinates; since
+ we're only interested in vertices, these will be 0 or 1. */
+ basevcol = bs->vcol;
+ if((vlr->flag & R_FACE_SPLIT) == 0) {
+ /* Processing triangle face, whole quad, or first half of split quad.
+ Vertex indices have to be corrected for possible split. */
+ i1= 0; i2= 1; i3= 2;
+ vlr_set_uv_indices(vlr, &i1, &i2, &i3);
+
+ bs->vcol= basevcol + i1;
+ do_bake_shade(bs, 0, 0, 1.0f, 0.0f);
+ bs->vcol= basevcol + i2;
+ do_bake_shade(bs, 0, 0, 0.0f, 1.0f);
+ bs->vcol= basevcol + i3;
+ do_bake_shade(bs, 0, 0, 0.0f, 0.0f);
+
+ if(vlr->v4) {
+ bs->quad= 1;
+ /* Find index to use for fourth vertex. */
+ for(i4=0; i4<4; i4++) {
+ if(i4 != i1 && i4 != i2 && i4 != i3)
+ break;
+ }
+ bs->vcol= basevcol + i4;
+ do_bake_shade(bs, 0, 0, 0.0f, 0.0f);
+ }
+ }
+ else {
+ /* Processing second half of split quad. Only one vertex to go. */
+ if(vlr->flag &R_DIVIDE_24) {
+ bs->vcol= basevcol + 2;
+ do_bake_shade(bs, 0, 0, 0.0f, 1.0f);
+ }
+ else {
+ bs->vcol= basevcol + 3;
+ do_bake_shade(bs, 0, 0, 0.0f, 0.0f);
+ }
+ }
+ bs->vcol = basevcol;
+}
+
+/* Bake a single face. This sets up data and a callback for zspan_scanconvert,
+ which does the rasterisation. */
/* already have tested for tface and ima and zspan */
static void shade_tface(BakeShade *bs)
{
@@ -2506,6 +2638,7 @@ static void shade_tface(BakeShade *bs)
bs->recty= bs->ibuf->y;
bs->rect= bs->ibuf->rect;
bs->rect_float= bs->ibuf->rect_float;
+ bs->vcol = NULL;
bs->quad= 0;
if (bs->usemask) {
@@ -2548,7 +2681,10 @@ static void *do_bake_thread(void *bs_v)
BakeShade *bs= bs_v;
while(get_next_bake_face(bs)) {
- shade_tface(bs);
+ if(R.r.bake_flag & R_BAKE_VCOL)
+ shade_verts(bs);
+ else
+ shade_tface(bs);
/* fast threadsafe break test */
if(R.test_break(R.tbh))
@@ -2606,15 +2742,17 @@ int RE_bake_shade_all_selected(Render *re, int type, Object *actob, short *do_up
/* do we need a mask? */
if (re->r.bake_filter)
usemask = 1;
-
- /* baker uses this flag to detect if image was initialized */
- for(ima= G.main->image.first; ima; ima= ima->id.next) {
- ImBuf *ibuf= BKE_image_get_ibuf(ima, NULL);
- ima->id.flag |= LIB_DOIT;
- if(ibuf) {
- ibuf->userdata = NULL; /* use for masking if needed */
- if(ibuf->rect_float)
- ibuf->profile = IB_PROFILE_LINEAR_RGB;
+
+ if((R.r.bake_flag & R_BAKE_VCOL) == 0) {
+ /* baker uses this flag to detect if image was initialized */
+ for(ima= G.main->image.first; ima; ima= ima->id.next) {
+ ImBuf *ibuf= BKE_image_get_ibuf(ima, NULL);
+ ima->id.flag |= LIB_DOIT;
+ if(ibuf) {
+ ibuf->userdata = NULL; /* use for masking if needed */
+ if(ibuf->rect_float)
+ ibuf->profile = IB_PROFILE_LINEAR_RGB;
+ }
}
}
@@ -2638,7 +2776,10 @@ int RE_bake_shade_all_selected(Render *re, int type, Object *actob, short *do_up
handles[a].type= type;
handles[a].actob= actob;
- handles[a].zspan= MEM_callocN(sizeof(ZSpan), "zspan for bake");
+ if(R.r.bake_flag & R_BAKE_VCOL)
+ handles[a].zspan= NULL;
+ else
+ handles[a].zspan= MEM_callocN(sizeof(ZSpan), "zspan for bake");
handles[a].usemask = usemask;
@@ -2664,25 +2805,27 @@ int RE_bake_shade_all_selected(Render *re, int type, Object *actob, short *do_up
}
}
- /* filter and refresh images */
- for(ima= G.main->image.first; ima; ima= ima->id.next) {
- if((ima->id.flag & LIB_DOIT)==0) {
- ImBuf *ibuf= BKE_image_get_ibuf(ima, NULL);
+ if((R.r.bake_flag & R_BAKE_VCOL) == 0) {
+ /* filter and refresh images */
+ for(ima= G.main->image.first; ima; ima= ima->id.next) {
+ if((ima->id.flag & LIB_DOIT)==0) {
+ ImBuf *ibuf= BKE_image_get_ibuf(ima, NULL);
- if(!ibuf)
- continue;
+ if(!ibuf)
+ continue;
- RE_bake_ibuf_filter(ibuf, (char *)ibuf->userdata, re->r.bake_filter);
+ RE_bake_ibuf_filter(ibuf, (char *)ibuf->userdata, re->r.bake_filter);
- ibuf->userflags |= IB_BITMAPDIRTY;
- if (ibuf->rect_float) IMB_rect_from_float(ibuf);
+ ibuf->userflags |= IB_BITMAPDIRTY;
+ if (ibuf->rect_float) IMB_rect_from_float(ibuf);
+ }
+ }
+
+ /* calculate return value */
+ for(a=0; a<re->r.threads; a++) {
+ zbuf_free_span(handles[a].zspan);
+ MEM_freeN(handles[a].zspan);
}
- }
-
- /* calculate return value */
- for(a=0; a<re->r.threads; a++) {
- zbuf_free_span(handles[a].zspan);
- MEM_freeN(handles[a].zspan);
}
MEM_freeN(handles);
diff --git a/source/blender/render/intern/source/renderdatabase.c b/source/blender/render/intern/source/renderdatabase.c
index 25575fa..83b2f22 100644
--- a/source/blender/render/intern/source/renderdatabase.c
+++ b/source/blender/render/intern/source/renderdatabase.c
@@ -108,6 +108,7 @@
#define RE_MTFACE_ELEMS 1
#define RE_MCOL_ELEMS 4
#define RE_UV_ELEMS 2
+#define RE_ORIGINDEX_ELEMS 1
#define RE_SURFNOR_ELEMS 3
#define RE_RADFACE_ELEMS 1
#define RE_SIMPLIFY_ELEMS 2
@@ -355,6 +356,21 @@ MCol *RE_vlakren_get_mcol(ObjectRen *obr, VlakRen *vlr, int n, char **name, int
return node->mcol + index*RE_MCOL_ELEMS;
}
+int *RE_vlakren_get_origindex(ObjectRen *obr, VlakRen *vlak, int verify)
+{
+ int *origindex;
+ int nr= vlak->index>>8;
+
+ origindex= obr->vlaknodes[nr].origindex;
+ if(origindex==NULL) {
+ if(verify)
+ origindex= obr->vlaknodes[nr].origindex= MEM_callocN(256*RE_ORIGINDEX_ELEMS*sizeof(int), "origindex table");
+ else
+ return NULL;
+ }
+ return origindex + (vlak->index & 255)*RE_ORIGINDEX_ELEMS;
+}
+
float *RE_vlakren_get_surfnor(ObjectRen *obr, VlakRen *vlak, int verify)
{
float *surfnor;
@@ -406,6 +422,7 @@ VlakRen *RE_vlakren_copy(ObjectRen *obr, VlakRen *vlr)
MTFace *mtface, *mtface1;
MCol *mcol, *mcol1;
float *surfnor, *surfnor1, *tangent, *tangent1;
+ int *origindex, *origindex1;
RadFace **radface, **radface1;
int i, index = vlr1->index;
char *name;
@@ -423,6 +440,13 @@ VlakRen *RE_vlakren_copy(ObjectRen *obr, VlakRen *vlr)
memcpy(mcol1, mcol, sizeof(MCol)*RE_MCOL_ELEMS);
}
+ origindex= RE_vlakren_get_origindex(obr, vlr, 0);
+ if(origindex) {
+ origindex1= RE_vlakren_get_origindex(obr, vlr1, 1);
+ /* Just an int, but memcpy for consistency. */
+ memcpy(origindex1, origindex, sizeof(int)*RE_ORIGINDEX_ELEMS);
+ }
+
surfnor= RE_vlakren_get_surfnor(obr, vlr, 0);
if(surfnor) {
surfnor1= RE_vlakren_get_surfnor(obr, vlr1, 1);
@@ -768,6 +792,8 @@ void free_renderdata_vlaknodes(VlakTableNode *vlaknodes)
MEM_freeN(vlaknodes[a].mtface);
if(vlaknodes[a].mcol)
MEM_freeN(vlaknodes[a].mcol);
+ if(vlaknodes[a].origindex)
+ MEM_freeN(vlaknodes[a].origindex);
if(vlaknodes[a].surfnor)
MEM_freeN(vlaknodes[a].surfnor);
if(vlaknodes[a].tangent)
diff --git a/source/blender/render/intern/source/shadeinput.c b/source/blender/render/intern/source/shadeinput.c
index 55d3703..c472240 100644
--- a/source/blender/render/intern/source/shadeinput.c
+++ b/source/blender/render/intern/source/shadeinput.c
@@ -238,7 +238,8 @@ void vlr_set_uv_indices(VlakRen *vlr, int *i1, int *i2, int *i3)
/* |0\ | |/ 0| */
/* 1---2 1---2 0 = orig face, 1 = new face */
- /* Update vert nums to point to correct verts of original face */
+ /* Update vert nums to point to correct verts of original face. See also
+ splitting logic in check_non_flat_quads. */
if(vlr->flag & R_DIVIDE_24) {
if(vlr->flag & R_FACE_SPLIT) {
(*i1)++; (*i2)++; (*i3)++;

Event Timeline