Ambient occlusion baker from multi-resolution mesh
authorSergey Sharybin <sergey.vfx@gmail.com>
Tue, 18 Dec 2012 17:46:42 +0000 (17:46 +0000)
committerSergey Sharybin <sergey.vfx@gmail.com>
Tue, 18 Dec 2012 17:46:42 +0000 (17:46 +0000)
This implements AO baking directly from multi-resolution mesh with much
less memory overhead than regular baker.

Uses rays distribution implementation from Morten Mikkelsen, raycast
is based on RayObject also used by Blender Internal.

Works in single-thread yet, multi-threading would be implemented later.

14 files changed:
intern/guardedalloc/intern/mallocn.c
release/scripts/startup/bl_ui/properties_render.py
source/blender/blenkernel/intern/scene.c
source/blender/blenloader/intern/readfile.c
source/blender/editors/object/object_bake.c
source/blender/makesdna/DNA_scene_types.h
source/blender/makesrna/intern/rna_scene.c
source/blender/render/extern/include/RE_multires_bake.h
source/blender/render/intern/include/rayobject.h
source/blender/render/intern/include/rendercore.h
source/blender/render/intern/raytrace/rayobject.cpp
source/blender/render/intern/source/multires_bake.c
source/blender/render/intern/source/rayshade.c
source/blender/render/intern/source/rendercore.c

index d747f04..11fa477 100644 (file)
@@ -561,6 +561,8 @@ void MEM_printmemlist_stats(void)
        qsort(printblock, totpb, sizeof(MemPrintBlock), compare_len);
        printf("\ntotal memory len: %.3f MB\n",
               (double)mem_in_use / (double)(1024 * 1024));
+       printf("peak memory len: %.3f MB\n",
+              (double)peak_mem / (double)(1024 * 1024));
        printf(" ITEMS TOTAL-MiB AVERAGE-KiB TYPE\n");
        for (a = 0, pb = printblock; a < totpb; a++, pb++) {
                printf("%6d (%8.3f  %8.3f) %s\n",
index 9814dd7..a33bc31 100644 (file)
@@ -573,7 +573,7 @@ class RENDER_PT_bake(RenderButtonsPanel, Panel):
         layout.prop(rd, "bake_type")
 
         multires_bake = False
-        if rd.bake_type in ['NORMALS', 'DISPLACEMENT']:
+        if rd.bake_type in ['NORMALS', 'DISPLACEMENT', 'AO']:
             layout.prop(rd, "use_bake_multires")
             multires_bake = rd.use_bake_multires
 
@@ -602,11 +602,19 @@ class RENDER_PT_bake(RenderButtonsPanel, Panel):
             sub.prop(rd, "bake_distance")
             sub.prop(rd, "bake_bias")
         else:
-            if rd.bake_type == 'DISPLACEMENT':
-                layout.prop(rd, "use_bake_lores_mesh")
+            split = layout.split()
 
-            layout.prop(rd, "use_bake_clear")
-            layout.prop(rd, "bake_margin")
+            col = split.column()
+            col.prop(rd, "use_bake_clear")
+            col.prop(rd, "bake_margin")
+
+            if rd.bake_type == 'DISPLACEMENT':
+                col = split.column()
+                col.prop(rd, "use_bake_lores_mesh")
+            if rd.bake_type == 'AO':
+                col = split.column()
+                col.prop(rd, "bake_bias")
+                col.prop(rd, "bake_rays_number")
 
 
 if __name__ == "__main__":  # only for live edit.
index 6a5886f..136f5d5 100644 (file)
@@ -432,6 +432,7 @@ static Scene *scene_add(Main *bmain, const char *name)
        sce->r.bake_osa = 5;
        sce->r.bake_flag = R_BAKE_CLEAR;
        sce->r.bake_normal_space = R_BAKE_SPACE_TANGENT;
+       sce->r.bake_rays_number = 256;
        sce->r.scemode = R_DOCOMP | R_DOSEQ | R_EXTENSION;
        sce->r.stamp = R_STAMP_TIME | R_STAMP_FRAME | R_STAMP_DATE | R_STAMP_CAMERA | R_STAMP_SCENE | R_STAMP_FILENAME | R_STAMP_RENDERTIME;
        sce->r.stamp_font_id = 12;
index 98f3baa..07cb3fd 100644 (file)
@@ -8420,6 +8420,14 @@ static void do_versions(FileData *fd, Library *lib, Main *main)
                }
        }
 
+       {
+               Scene *scene;
+
+               for (scene = main->scene.first; scene; scene = scene->id.next) {
+                       if (scene->r.bake_rays_number == 0)
+                               scene->r.bake_rays_number = 256;
+               }
+       }
        /* WATCH IT!!!: pointers from libdata have not been converted yet here! */
        /* WATCH IT 2!: Userdef struct init has to be in editors/interface/resources.c! */
 
index e9b4a7e..77d31a9 100644 (file)
@@ -96,6 +96,9 @@ typedef struct {
        ListBase data;
        int bake_clear, bake_filter;
        short mode, use_lores_mesh;
+       int number_of_rays;
+       float bias;
+       int raytrace_structure;
 } MultiresBakeJob;
 
 static int multiresbake_check(bContext *C, wmOperator *op)
@@ -203,6 +206,9 @@ static DerivedMesh *multiresbake_create_loresdm(Scene *scene, Object *ob, int *l
 
        if (*lvl == 0) {
                DerivedMesh *tmp_dm = CDDM_from_mesh(me, ob);
+
+               DM_set_only_copy(tmp_dm, CD_MASK_BAREMESH | CD_MASK_MTFACE);
+
                dm = CDDM_copy(tmp_dm);
                tmp_dm->release(tmp_dm);
        }
@@ -210,6 +216,8 @@ static DerivedMesh *multiresbake_create_loresdm(Scene *scene, Object *ob, int *l
                MultiresModifierData tmp_mmd = *mmd;
                DerivedMesh *cddm = CDDM_from_mesh(me, ob);
 
+               DM_set_only_copy(cddm, CD_MASK_BAREMESH | CD_MASK_MTFACE);
+
                tmp_mmd.lvl = *lvl;
                tmp_mmd.sculptlvl = *lvl;
                dm = multires_make_derived_from_derived(cddm, &tmp_mmd, ob, 0);
@@ -227,6 +235,14 @@ static DerivedMesh *multiresbake_create_hiresdm(Scene *scene, Object *ob, int *l
        DerivedMesh *cddm = CDDM_from_mesh(me, ob);
        DerivedMesh *dm;
 
+       DM_set_only_copy(cddm, CD_MASK_BAREMESH);
+
+       /* TODO: DM_set_only_copy wouldn't set mask for loop and poly data,
+        *       but we really need BAREMESH only to save lots of memory
+        */
+       CustomData_set_only_copy(&cddm->loopData, CD_MASK_BAREMESH);
+       CustomData_set_only_copy(&cddm->polyData, CD_MASK_BAREMESH);
+
        *lvl = mmd->totlvl;
        *simple = mmd->simple;
 
@@ -298,14 +314,13 @@ static int multiresbake_image_exec_locked(bContext *C, wmOperator *op)
                bkr.bake_filter = scene->r.bake_filter;
                bkr.mode = scene->r.bake_mode;
                bkr.use_lores_mesh = scene->r.bake_flag & R_BAKE_LORES_MESH;
+               bkr.bias = scene->r.bake_biasdist;
+               bkr.number_of_rays = scene->r.bake_rays_number;
+               bkr.raytrace_structure = scene->r.raytrace_structure;
 
                /* create low-resolution DM (to bake to) and hi-resolution DM (to bake from) */
-               bkr.lores_dm = multiresbake_create_loresdm(scene, ob, &bkr.lvl);
-
-               if (!bkr.lores_dm)
-                       continue;
-
                bkr.hires_dm = multiresbake_create_hiresdm(scene, ob, &bkr.tot_lvl, &bkr.simple);
+               bkr.lores_dm = multiresbake_create_loresdm(scene, ob, &bkr.lvl);
 
                RE_multires_bake_images(&bkr);
 
@@ -335,24 +350,25 @@ static void init_multiresbake_job(bContext *C, MultiresBakeJob *bkj)
        bkj->mode = scene->r.bake_mode;
        bkj->use_lores_mesh = scene->r.bake_flag & R_BAKE_LORES_MESH;
        bkj->bake_clear = scene->r.bake_flag & R_BAKE_CLEAR;
+       bkj->bias = scene->r.bake_biasdist;
+       bkj->number_of_rays = scene->r.bake_rays_number;
+       bkj->raytrace_structure = scene->r.raytrace_structure;
 
        CTX_DATA_BEGIN (C, Base *, base, selected_editable_bases)
        {
                MultiresBakerJobData *data;
-               DerivedMesh *lores_dm;
                int lvl;
+
                ob = base->object;
 
                multires_force_update(ob);
 
-               lores_dm = multiresbake_create_loresdm(scene, ob, &lvl);
-               if (!lores_dm)
-                       continue;
-
                data = MEM_callocN(sizeof(MultiresBakerJobData), "multiresBaker derivedMesh_data");
-               data->lores_dm = lores_dm;
-               data->lvl = lvl;
+
+               /* create low-resolution DM (to bake to) and hi-resolution DM (to bake from) */
                data->hires_dm = multiresbake_create_hiresdm(scene, ob, &data->tot_lvl, &data->simple);
+               data->lores_dm = multiresbake_create_loresdm(scene, ob, &lvl);
+               data->lvl = lvl;
 
                BLI_addtail(&bkj->data, data);
        }
@@ -399,6 +415,10 @@ static void multiresbake_startjob(void *bkv, short *stop, short *do_update, floa
                bkr.do_update = do_update;
                bkr.progress = progress;
 
+               bkr.bias = bkj->bias;
+               bkr.number_of_rays = bkj->number_of_rays;
+               bkr.raytrace_structure = bkj->raytrace_structure;
+
                RE_multires_bake_images(&bkr);
 
                BLI_freelistN(&bkr.image);
@@ -652,7 +672,7 @@ static int objects_bake_render_modal(bContext *C, wmOperator *UNUSED(op), wmEven
 
 static int is_multires_bake(Scene *scene)
 {
-       if (ELEM(scene->r.bake_mode, RE_BAKE_NORMALS, RE_BAKE_DISPLACEMENT))
+       if (ELEM3(scene->r.bake_mode, RE_BAKE_NORMALS, RE_BAKE_DISPLACEMENT, RE_BAKE_AO))
                return scene->r.bake_flag & R_BAKE_MULTIRES;
 
        return 0;
index 988517f..b3aabf1 100644 (file)
@@ -491,7 +491,8 @@ typedef struct RenderData {
        /* Bake Render options */
        short bake_osa, bake_filter, bake_mode, bake_flag;
        short bake_normal_space, bake_quad_split;
-       float bake_maxdist, bake_biasdist, bake_pad;
+       float bake_maxdist, bake_biasdist;
+       int bake_rays_number;
 
        /* path to render output */
        char pic[1024]; /* 1024 = FILE_MAX */
index 8f31b17..fad4a20 100644 (file)
@@ -3881,6 +3881,13 @@ static void rna_def_scene_render_data(BlenderRNA *brna)
                                 "Calculate heights against unsubdivided low resolution mesh");
        RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
 
+       prop = RNA_def_property(srna, "bake_rays_number", PROP_INT, PROP_NONE);
+       RNA_def_property_int_sdna(prop, NULL, "bake_rays_number");
+       RNA_def_property_range(prop, 64, 1024);
+       RNA_def_property_int_default(prop, 256);
+       RNA_def_property_ui_text(prop, "Number of Rays", "Number of rays used for ambient occlusion baking from multires");
+       RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
+
        /* stamp */
        
        prop = RNA_def_property(srna, "use_stamp_time", PROP_BOOLEAN, PROP_NONE);
index bb9c9b8..31fe939 100644 (file)
@@ -40,11 +40,16 @@ typedef struct MultiresBakeRender {
        int simple, lvl, tot_lvl, bake_filter;
        short mode, use_lores_mesh;
 
+       int number_of_rays;
+       float bias;
+
        int tot_obj, tot_image;
        ListBase image;
 
        int baked_objects, baked_faces;
 
+       int raytrace_structure;
+
        short *stop;
        short *do_update;
        float *progress;
index 7a9d242..e9514b8 100644 (file)
@@ -87,6 +87,8 @@ typedef struct RayFace {
 
 RayObject *RE_rayface_from_vlak(RayFace *face, struct ObjectInstanceRen *obi, struct VlakRen *vlr);
 
+RayObject *RE_rayface_from_coords(RayFace *rayface, void *ob, void *face, float *v1, float *v2, float *v3, float *v4);
+
 /* RayObject representing faces directly from a given VlakRen structure. Thus
  * allowing to save memory, but making code triangle intersection dependent on
  * render structures. */
index 3071225..921f117 100644 (file)
@@ -83,6 +83,8 @@ int get_sample_layers(struct RenderPart *pa, struct RenderLayer *rl, struct Rend
 
 /* -------- ray.c ------- */
 
+struct RayObject *RE_rayobject_create(struct Render *re, int type, int size);
+
 extern void freeraytree(Render *re);
 extern void makeraytree(Render *re);
 struct RayObject* makeraytree_object(Render *re, ObjectInstanceRen *obi);
index c3babf9..b31aff8 100644 (file)
@@ -90,6 +90,11 @@ RayObject *RE_rayface_from_vlak(RayFace *rayface, ObjectInstanceRen *obi, VlakRe
        return rayface_from_coords(rayface, obi, vlr, vlr->v1->co, vlr->v2->co, vlr->v3->co, vlr->v4 ? vlr->v4->co : 0);
 }
 
+RayObject *RE_rayface_from_coords(RayFace *rayface, void *ob, void *face, float *v1, float *v2, float *v3, float *v4)
+{
+       return rayface_from_coords(rayface, ob, face, v1, v2, v3, v4);
+}
+
 /* VlakPrimitive */
 
 RayObject *RE_vlakprimitive_from_vlak(VlakPrimitive *face, struct ObjectInstanceRen *obi, struct VlakRen *vlr)
index f84fcf9..55fb8ae 100644 (file)
 #include "IMB_imbuf_types.h"
 #include "IMB_imbuf.h"
 
+#include "rayintersection.h"
+#include "rayobject.h"
+#include "rendercore.h"
+
 typedef void (*MPassKnownData)(DerivedMesh *lores_dm, DerivedMesh *hires_dm, const void *bake_data,
                                ImBuf *ibuf, const int face_index, const int lvl, const float st[2],
                                float tangmat[3][3], const int x, const int y);
@@ -102,6 +106,20 @@ typedef struct {
        const int *orig_index_mp_to_orig;
 } MNormalBakeData;
 
+typedef struct {
+       int number_of_rays;
+       float bias;
+
+       unsigned short *permutation_table_1;
+       unsigned short *permutation_table_2;
+
+       RayObject *raytree;
+       RayFace *rayfaces;
+
+       const int *orig_index_mf_to_mpoly;
+       const int *orig_index_mp_to_orig;
+} MAOBakeData;
+
 static void multiresbake_get_normal(const MResolvePixelData *data, float norm[], const int face_num, const int vert_index)
 {
        unsigned int indices[] = {data->mface[face_num].v1, data->mface[face_num].v2,
@@ -169,10 +187,6 @@ static void flush_pixel(const MResolvePixelData *data, const int x, const int y)
        st1 = data->mtface[data->face_index].uv[i1];
        st2 = data->mtface[data->face_index].uv[i2];
 
-       tang0 = data->pvtangent + data->face_index * 16 + i0 * 4;
-       tang1 = data->pvtangent + data->face_index * 16 + i1 * 4;
-       tang2 = data->pvtangent + data->face_index * 16 + i2 * 4;
-
        multiresbake_get_normal(data, no0, data->face_index, i0);   /* can optimize these 3 into one call */
        multiresbake_get_normal(data, no1, data->face_index, i1);
        multiresbake_get_normal(data, no2, data->face_index, i2);
@@ -183,21 +197,29 @@ static void flush_pixel(const MResolvePixelData *data, const int x, const int y)
        v = fUV[1];
        w = 1 - u - v;
 
-       /* the sign is the same at all face vertices for any non degenerate face.
-        * Just in case we clamp the interpolated value though. */
-       sign = (tang0[3] * u + tang1[3] * v + tang2[3] * w) < 0 ? (-1.0f) : 1.0f;
+       if (data->pvtangent) {
+               tang0 = data->pvtangent + data->face_index * 16 + i0 * 4;
+               tang1 = data->pvtangent + data->face_index * 16 + i1 * 4;
+               tang2 = data->pvtangent + data->face_index * 16 + i2 * 4;
 
-       /* this sequence of math is designed specifically as is with great care
-        * to be compatible with our shader. Please don't change without good reason. */
-       for (r = 0; r < 3; r++) {
-               from_tang[0][r] = tang0[r] * u + tang1[r] * v + tang2[r] * w;
-               from_tang[2][r] = no0[r] * u + no1[r] * v + no2[r] * w;
-       }
+               /* the sign is the same at all face vertices for any non degenerate face.
+                * Just in case we clamp the interpolated value though. */
+               sign = (tang0[3] * u + tang1[3] * v + tang2[3] * w) < 0 ? (-1.0f) : 1.0f;
+
+               /* this sequence of math is designed specifically as is with great care
+                * to be compatible with our shader. Please don't change without good reason. */
+               for (r = 0; r < 3; r++) {
+                       from_tang[0][r] = tang0[r] * u + tang1[r] * v + tang2[r] * w;
+                       from_tang[2][r] = no0[r] * u + no1[r] * v + no2[r] * w;
+               }
 
-       cross_v3_v3v3(from_tang[1], from_tang[2], from_tang[0]);  /* B = sign * cross(N, T)  */
-       mul_v3_fl(from_tang[1], sign);
-       invert_m3_m3(to_tang, from_tang);
-       /* sequence end */
+               cross_v3_v3v3(from_tang[1], from_tang[2], from_tang[0]);  /* B = sign * cross(N, T)  */
+               mul_v3_fl(from_tang[1], sign);
+               invert_m3_m3(to_tang, from_tang);
+       }
+       else {
+               zero_m3(to_tang);
+       }
 
        data->pass_data(data->lores_dm, data->hires_dm, data->bake_data,
                        data->ibuf, data->face_index, data->lvl, st, to_tang, x, y);
@@ -307,7 +329,7 @@ static int multiresbake_test_break(MultiresBakeRender *bkr)
        return G.is_break;
 }
 
-static void do_multires_bake(MultiresBakeRender *bkr, Image *ima, MPassKnownData passKnownData,
+static void do_multires_bake(MultiresBakeRender *bkr, Image *ima, int require_tangent, MPassKnownData passKnownData,
                              MInitBakeData initBakeData, MApplyBakeData applyBakeData, MFreeBakeData freeBakeData)
 {
        DerivedMesh *dm = bkr->lores_dm;
@@ -319,10 +341,12 @@ static void do_multires_bake(MultiresBakeRender *bkr, Image *ima, MPassKnownData
        MTFace *mtface = dm->getTessFaceDataArray(dm, CD_MTFACE);
        float *pvtangent = NULL;
 
-       if (CustomData_get_layer_index(&dm->faceData, CD_TANGENT) == -1)
-               DM_add_tangent_layer(dm);
+       if (require_tangent) {
+               if (CustomData_get_layer_index(&dm->faceData, CD_TANGENT) == -1)
+                       DM_add_tangent_layer(dm);
 
-       pvtangent = DM_get_tessface_data_layer(dm, CD_TANGENT);
+               pvtangent = DM_get_tessface_data_layer(dm, CD_TANGENT);
+       }
 
        if (tot_face > 0) {  /* sanity check */
                int f = 0;
@@ -769,6 +793,298 @@ static void apply_tangmat_callback(DerivedMesh *lores_dm, DerivedMesh *hires_dm,
        ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID;
 }
 
+/* **************** Ambient Occlusion Baker **************** */
+
+#define MAX_NUMBER_OF_AO_RAYS 1024
+
+static unsigned short ao_random_table_1[MAX_NUMBER_OF_AO_RAYS];
+static unsigned short ao_random_table_2[MAX_NUMBER_OF_AO_RAYS];
+
+static void init_ao_random(void)
+{
+       int i;
+
+       for (i = 0; i < MAX_NUMBER_OF_AO_RAYS; i++) {
+               ao_random_table_1[i] = rand() & 0xffff;
+               ao_random_table_2[i] = rand() & 0xffff;
+       }
+}
+
+static unsigned short get_ao_random1(const int i)
+{
+       return ao_random_table_1[i & (MAX_NUMBER_OF_AO_RAYS - 1)];
+}
+
+static unsigned short get_ao_random2(const int i)
+{
+       return ao_random_table_2[i & (MAX_NUMBER_OF_AO_RAYS - 1)];
+}
+
+static void build_permutation_table(unsigned short permutation[], unsigned short temp_permutation[],
+                                    const int number_of_rays, const int is_first_perm_table)
+{
+       int i, k;
+
+       for (i = 0; i < number_of_rays; i++)
+               temp_permutation[i] = i;
+
+       for (i = 0; i < number_of_rays; i++) {
+               const unsigned int nr_entries_left = number_of_rays - i;
+               unsigned short rnd = is_first_perm_table != FALSE ? get_ao_random1(i) : get_ao_random2(i);
+               const unsigned short entry = rnd % nr_entries_left;
+
+               /* pull entry */
+               permutation[i] = temp_permutation[entry];
+
+               /* delete entry */
+               for(k = entry; k < nr_entries_left - 1; k++)
+                       temp_permutation[k] = temp_permutation[k + 1];
+       }
+
+       /* verify permutation table
+        * every entry must appear exactly once
+        */
+#if 0
+       for(i = 0; i < number_of_rays; i++) temp_permutation[i] = 0;
+       for(i = 0; i < number_of_rays; i++) ++temp_permutation[permutation[i]];
+       for(i = 0; i < number_of_rays; i++) BLI_assert(temp_permutation[i] == 1);
+#endif
+}
+
+static void create_ao_raytree(MultiresBakeRender *bkr, MAOBakeData *ao_data)
+{
+       DerivedMesh *hidm = bkr->hires_dm;
+       RayObject *raytree;
+       RayFace *face;
+       CCGElem **grid_data;
+       CCGKey key;
+       int num_grids, grid_size, face_side, num_faces;
+       int i;
+
+       num_grids = hidm->getNumGrids(hidm);
+       grid_size = hidm->getGridSize(hidm);
+       grid_data = hidm->getGridData(hidm);
+       hidm->getGridKey(hidm, &key);
+
+       face_side = (grid_size << 1) - 1;
+       num_faces = num_grids * (grid_size - 1) * (grid_size - 1);
+
+       raytree = ao_data->raytree = RE_rayobject_create(NULL, bkr->raytrace_structure, num_faces);
+       face = ao_data->rayfaces = (RayFace *) MEM_callocN(num_faces * sizeof(RayFace), "ObjectRen faces");
+
+       for (i = 0; i < num_grids; i++) {
+               int x, y;
+               for (x = 0; x < grid_size - 1; x++) {
+                       for (y = 0; y < grid_size - 1; y++) {
+                               float co[4][3];
+
+                               copy_v3_v3(co[0], CCG_grid_elem_co(&key, grid_data[i], x, y));
+                               copy_v3_v3(co[1], CCG_grid_elem_co(&key, grid_data[i], x, y + 1));
+                               copy_v3_v3(co[2], CCG_grid_elem_co(&key, grid_data[i], x + 1, y + 1));
+                               copy_v3_v3(co[3], CCG_grid_elem_co(&key, grid_data[i], x + 1, y));
+
+                               RE_rayface_from_coords(face, ao_data, face, co[0], co[1], co[2], co[3]);
+                               RE_rayobject_add(raytree, RE_rayobject_unalignRayFace(face));
+
+                               face++;
+                       }
+               }
+       }
+
+       RE_rayobject_done(raytree);
+}
+
+static void *init_ao_data(MultiresBakeRender *bkr, Image *UNUSED(ima))
+{
+       MAOBakeData *ao_data;
+       DerivedMesh *lodm = bkr->lores_dm;
+       unsigned short *temp_permutation_table;
+       size_t permutation_size;
+
+       init_ao_random();
+
+       ao_data = MEM_callocN(sizeof(MAOBakeData), "MultiresBake aoData");
+
+       ao_data->number_of_rays = bkr->number_of_rays;
+       ao_data->bias = bkr->bias;
+
+       ao_data->orig_index_mf_to_mpoly = lodm->getTessFaceDataArray(lodm, CD_ORIGINDEX);
+       ao_data->orig_index_mp_to_orig = lodm->getPolyDataArray(lodm, CD_ORIGINDEX);
+
+       create_ao_raytree(bkr, ao_data);
+
+       /* initialize permutation tables */
+       permutation_size = sizeof(unsigned short) * bkr->number_of_rays;
+       ao_data->permutation_table_1 = MEM_callocN(permutation_size, "multires AO baker perm1");
+       ao_data->permutation_table_2 = MEM_callocN(permutation_size, "multires AO baker perm2");
+       temp_permutation_table = MEM_callocN(permutation_size, "multires AO baker temp perm");
+
+       build_permutation_table(ao_data->permutation_table_1, temp_permutation_table, bkr->number_of_rays, 1);
+       build_permutation_table(ao_data->permutation_table_2, temp_permutation_table, bkr->number_of_rays, 0);
+
+       MEM_freeN(temp_permutation_table);
+
+       return (void *)ao_data;
+}
+
+static void free_ao_data(void *bake_data)
+{
+       MAOBakeData *ao_data = (MAOBakeData *) bake_data;
+
+       RE_rayobject_free(ao_data->raytree);
+       MEM_freeN(ao_data->rayfaces);
+
+       MEM_freeN(ao_data->permutation_table_1);
+       MEM_freeN(ao_data->permutation_table_2);
+
+       MEM_freeN(ao_data);
+}
+
+/* builds an X and a Y axis from the given Z axis */
+static void build_coordinate_frame(float axisX[3], float axisY[3], const float axisZ[3])
+{
+       const float faX = fabsf(axisZ[0]);
+       const float faY = fabsf(axisZ[1]);
+       const float faZ = fabsf(axisZ[2]);
+
+       if (faX <= faY && faX <= faZ) {
+               const float len = sqrtf(axisZ[1] * axisZ[1] + axisZ[2] * axisZ[2]);
+               axisY[0] = 0; axisY[1] = axisZ[2] / len; axisY[2] = -axisZ[1] / len;
+               cross_v3_v3v3(axisX, axisY, axisZ);
+       }
+       else if (faY <= faZ) {
+               const float len = sqrtf(axisZ[0] * axisZ[0] + axisZ[2] * axisZ[2]);
+               axisX[0] = axisZ[2] / len; axisX[1] = 0; axisX[2] = -axisZ[0] / len;
+               cross_v3_v3v3(axisY, axisZ, axisX);
+       }
+       else {
+               const float len = sqrtf(axisZ[0] * axisZ[0] + axisZ[1] * axisZ[1]);
+               axisX[0] = axisZ[1] / len; axisX[1] = -axisZ[0] / len; axisX[2] = 0;
+               cross_v3_v3v3(axisY, axisZ, axisX);
+       }
+}
+
+/* return FALSE if nothing was hit and TRUE otherwise */
+static int trace_ao_ray(MAOBakeData *ao_data, float ray_start[3], float ray_direction[3])
+{
+       Isect isect = {{0}};
+
+       isect.dist = RE_RAYTRACE_MAXDIST;
+       copy_v3_v3(isect.start, ray_start);
+       copy_v3_v3(isect.dir, ray_direction);
+       isect.lay = -1;
+
+       normalize_v3(isect.dir);
+
+       return RE_rayobject_raycast(ao_data->raytree, &isect);
+}
+
+static void apply_ao_callback(DerivedMesh *lores_dm, DerivedMesh *hires_dm, const void *bake_data,
+                              ImBuf *ibuf, const int face_index, const int lvl, const float st[2],
+                              float UNUSED(tangmat[3][3]), const int x, const int y)
+{
+       MAOBakeData *ao_data = (MAOBakeData *) bake_data;
+       MTFace *mtface = CustomData_get_layer(&lores_dm->faceData, CD_MTFACE);
+       MFace mface;
+
+       int i, k, perm_offs;
+       float pos[3], nrm[3];
+       float cen[3];
+       float axisX[3], axisY[3], axisZ[3];
+       float shadow = 0;
+       float value;
+       int pixel = ibuf->x * y + x;
+       float uv[2], *st0, *st1, *st2, *st3;
+
+       lores_dm->getTessFace(lores_dm, face_index, &mface);
+
+       st0 = mtface[face_index].uv[0];
+       st1 = mtface[face_index].uv[1];
+       st2 = mtface[face_index].uv[2];
+
+       if (mface.v4) {
+               st3 = mtface[face_index].uv[3];
+               resolve_quad_uv(uv, st, st0, st1, st2, st3);
+       }
+       else
+               resolve_tri_uv(uv, st, st0, st1, st2);
+
+       CLAMP(uv[0], 0.0f, 1.0f);
+       CLAMP(uv[1], 0.0f, 1.0f);
+
+       get_ccgdm_data(lores_dm, hires_dm,
+                      ao_data->orig_index_mf_to_mpoly, ao_data->orig_index_mp_to_orig,
+                      lvl, face_index, uv[0], uv[1], pos, nrm);
+
+       /* offset ray origin by user bias along normal */
+       for (i = 0; i < 3; i++)
+               cen[i] = pos[i] + ao_data->bias * nrm[i];
+
+       /* build tangent frame */
+       for (i = 0; i < 3; i++)
+               axisZ[i] = nrm[i];
+
+       build_coordinate_frame(axisX, axisY, axisZ);
+
+       /* static noise */
+       perm_offs = (get_ao_random2(get_ao_random1(x) + y)) & (MAX_NUMBER_OF_AO_RAYS - 1);
+
+       /* importance sample shadow rays (cosine weighted) */
+       for (i = 0; i < ao_data->number_of_rays; i++) {
+               int hit_something;
+
+               /* use N-Rooks to distribute our N ray samples across
+                * a multi-dimensional domain (2D)
+                */
+               const unsigned short I = ao_random_table_1[(i + perm_offs) % ao_data->number_of_rays];
+               const unsigned short J = ao_random_table_2[i];
+
+               const float JitPh = (get_ao_random2(I + perm_offs) & (MAX_NUMBER_OF_AO_RAYS-1))/((float) MAX_NUMBER_OF_AO_RAYS);
+               const float JitTh = (get_ao_random1(J + perm_offs) & (MAX_NUMBER_OF_AO_RAYS-1))/((float) MAX_NUMBER_OF_AO_RAYS);
+               const float SiSqPhi = (I + JitPh) / ao_data->number_of_rays;
+               const float Theta = 2 * M_PI * ((J + JitTh) / ao_data->number_of_rays);
+
+               /* this gives results identical to the so-called cosine
+                * weighted distribution relative to the north pole.
+                */
+               float SiPhi = sqrt(SiSqPhi);
+               float CoPhi = SiSqPhi < 1.0f ? sqrt(1.0f - SiSqPhi) : 1.0f - SiSqPhi;
+               float CoThe = cos(Theta);
+               float SiThe = sin(Theta);
+
+               const float dx = CoThe * CoPhi;
+               const float dy = SiThe * CoPhi;
+               const float dz = SiPhi;
+
+               /* transform ray direction out of tangent frame */
+               float dv[3];
+               for (k = 0; k < 3; k++)
+                       dv[k] = axisX[k] * dx + axisY[k] * dy + axisZ[k] * dz;
+
+               hit_something = trace_ao_ray(ao_data, cen, dv);
+
+               if (hit_something != 0)
+                       shadow += 1;
+       }
+
+       value = 1.0f - (shadow / ao_data->number_of_rays);
+
+       if (ibuf->rect_float) {
+               float *rrgbf = ibuf->rect_float + pixel * 4;
+               rrgbf[0] = rrgbf[1] = rrgbf[2] = value;
+               rrgbf[3] = 1.0f;
+
+               ibuf->userflags = IB_RECT_INVALID;
+       }
+       else {
+               unsigned char *rrgb = (unsigned char *) ibuf->rect + pixel * 4;
+               rrgb[0] = rrgb[1] = rrgb[2] = FTOCHAR(value);
+               rrgb[3] = 255;
+       }
+
+       ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID;
+}
+
 /* **************** Common functions public API relates on **************** */
 
 static void count_images(MultiresBakeRender *bkr)
@@ -812,12 +1128,15 @@ static void bake_images(MultiresBakeRender *bkr)
 
                        switch (bkr->mode) {
                                case RE_BAKE_NORMALS:
-                                       do_multires_bake(bkr, ima, apply_tangmat_callback, init_normal_data, NULL, free_normal_data);
+                                       do_multires_bake(bkr, ima, TRUE, apply_tangmat_callback, init_normal_data, NULL, free_normal_data);
                                        break;
                                case RE_BAKE_DISPLACEMENT:
-                                       do_multires_bake(bkr, ima, apply_heights_callback, init_heights_data,
+                                       do_multires_bake(bkr, ima, FALSE, apply_heights_callback, init_heights_data,
                                                         apply_heights_data, free_heights_data);
                                        break;
+                               case RE_BAKE_AO:
+                                       do_multires_bake(bkr, ima, FALSE, apply_ao_callback, init_ao_data, NULL, free_ao_data);
+                                       break;
                        }
                }
 
index 127e0bc..1224edd 100644 (file)
@@ -94,7 +94,7 @@ static void RE_rayobject_config_control(RayObject *r, Render *re)
        }
 }
 
-static RayObject*  RE_rayobject_create(Render *re, int type, int size)
+RayObject* RE_rayobject_create(Render *re, int type, int size)
 {
        RayObject * res = NULL;
 
@@ -117,7 +117,7 @@ static RayObject*  RE_rayobject_create(Render *re, int type, int size)
        
                
        if (type == R_RAYSTRUCTURE_OCTREE) //TODO dynamic ocres
-               res = RE_rayobject_octree_create(re->r.ocres, size);
+               res = RE_rayobject_octree_create(re ? re->r.ocres : 128, size);
        else if (type == R_RAYSTRUCTURE_BLIBVH)
                res = RE_rayobject_blibvh_create(size);
        else if (type == R_RAYSTRUCTURE_VBVH)
@@ -130,7 +130,7 @@ static RayObject*  RE_rayobject_create(Render *re, int type, int size)
                res = RE_rayobject_vbvh_create(size);   //Fallback
        
        
-       if (res)
+       if (res && re)
                RE_rayobject_config_control(res, re);
        
        return res;
index 3431c3f..bd0061c 100644 (file)
@@ -2795,4 +2795,3 @@ struct Image *RE_bake_shade_get_image(void)
 {
        return R.bakebuf;
 }
-