[Bf-blender-cvs] [610a3e2] soc-2014-shapekey: Implemented mesh shape key compression

Grigory Revzin noreply at git.blender.org
Sat Jun 14 01:42:34 CEST 2014


Commit: 610a3e288e9dd502dc0eb73c528e05eba9a9ea40
Author: Grigory Revzin
Date:   Sat Jun 14 03:42:17 2014 +0400
https://developer.blender.org/rB610a3e288e9dd502dc0eb73c528e05eba9a9ea40

Implemented mesh shape key compression

Mesh shape keys are now compressed on file write by storing only the vertices that have changed from the rest position (the logic automatically determines if there will be a gain from such compression, if not, no compression will happen).

This showed 3x disk space reduction on Elizabeth's head here (4.5 kvert mesh, 157 shape keys for different facial expression):
http://dl.dropboxusercontent.com/u/91422001/liz_headonly_masterbranch.blend
http://cs617325.vk.me/v617325382/e0e1/cjnMXhbtU44.jpg

A User Preference (on File tab) has been added to save the shape keys in the old format (compressed shape keys won't be readable from Master branch)

This also decreases object mode undo memory usage, as undo uses a file in memory.

TODO: endian conversions won't work on compressed keys right now.

===================================================================

M	release/scripts/startup/bl_ui/space_userpref.py
M	source/blender/blenloader/intern/readfile.c
M	source/blender/blenloader/intern/writefile.c
M	source/blender/makesdna/DNA_key_types.h
M	source/blender/makesdna/DNA_userdef_types.h
M	source/blender/makesrna/intern/rna_userdef.c

===================================================================

diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py
index bc478ed..b127bde 100644
--- a/release/scripts/startup/bl_ui/space_userpref.py
+++ b/release/scripts/startup/bl_ui/space_userpref.py
@@ -911,6 +911,7 @@ class USERPREF_PT_file(Panel):
         col.prop(paths, "hide_recent_locations")
         col.prop(paths, "hide_system_bookmarks")
         col.prop(paths, "show_thumbnails")
+        col.prop(paths, "legacy_keyblocks")
 
         col.separator()
 
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index abee8f3..e2aca7c 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -3036,6 +3036,33 @@ static void lib_link_key(FileData *fd, Main *main)
 	}
 }
 
+void uncompress_kb(Key * key, KeyBlock *kb) 
+{
+	int a, index;
+	float(*kbco)[3];
+
+	KeyBlock *rk = key->refkey;
+	KB_ComprMeshDataEnt *kbcde = kb->data; 
+
+	/* allocate space for uncompressed data */
+	kb->data = MEM_mallocN(sizeof(float) * 3 * rk->totelem, "KeyBlock");
+	kbco = kb->data;
+
+	/* step one: init with ref values */
+	memcpy(kb->data, rk->data, sizeof(float) * 3 * rk->totelem);
+	
+	/* step two: overwrite the saved vertices */
+	for (a = 0; a < kb->totelem; ++a) {
+		index = kbcde[a].vertex_index;
+		copy_v3_v3(kbco[index], kbcde[a].co);
+	}
+
+	kb->totelem = rk->totelem;
+
+	/* free compressed data */
+	MEM_freeN(kbcde);
+}
+
 static void switch_endian_keyblock(Key *key, KeyBlock *kb)
 {
 	int elemsize, a, b;
@@ -3081,9 +3108,17 @@ static void direct_link_key(FileData *fd, Key *key)
 
 	for (kb = key->block.first; kb; kb = kb->next) {
 		kb->data = newdataadr(fd, kb->data);
+
+		if (kb->compressed && key->refkey != kb) {
+			uncompress_kb(key, kb);
+			printf("Unpacked kb %s\n", kb->name);
+		}
 		
-		if (fd->flags & FD_FLAGS_SWITCH_ENDIAN)
+		if (fd->flags & FD_FLAGS_SWITCH_ENDIAN) {
+			BLI_assert(0); /* not supported with compressing yet */
 			switch_endian_keyblock(key, kb);
+		}
+
 	}
 }
 
diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c
index f822f3c..ebd70f2 100644
--- a/source/blender/blenloader/intern/writefile.c
+++ b/source/blender/blenloader/intern/writefile.c
@@ -144,6 +144,7 @@
 #include "BLI_blenlib.h"
 #include "BLI_linklist.h"
 #include "BLI_mempool.h"
+#include "BLI_math.h"		// shape key compression
 
 #include "BKE_action.h"
 #include "BKE_blender.h"
@@ -161,6 +162,7 @@
 #include "BKE_fcurve.h"
 #include "BKE_pointcache.h"
 #include "BKE_mesh.h"
+#include "BKE_key.h" // shape key compression
 
 #ifdef USE_NODE_COMPAT_CUSTOMNODES
 #include "NOD_common.h"
@@ -1575,12 +1577,68 @@ static void write_vfonts(WriteData *wd, ListBase *idbase)
 }
 
 
+/* ========================== shape keys =============================== */
+
+void compress_kb(KeyBlock *kb, Key *key_owner)
+{
+	/* the idea: we can get a space win by storing only the vertices with changed positions */
+	int a, changed_verts;
+	float diff[3];
+	KeyBlock *rk = key_owner->refkey;
+	float (*kbco)[3] = kb->data;
+	float (*rkbco)[3] = rk->data;
+
+	KB_ComprMeshDataEnt *kbcde = MEM_callocN(sizeof(KB_ComprMeshDataEnt) * rk->totelem, __func__);
+
+	BLI_assert(kb->data); /* should not happen at any time! */
+
+	changed_verts = 0; /* counts CompMeshDataEntries as well */
+	for (a = 0; a < rk->totelem; ++a) {
+		sub_v3_v3v3(diff, rkbco[a], kbco[a]);
+		if (len_squared_v3(diff) > 0.0001f) {
+			/* this vert's pos has changed from the base */
+			copy_v3_v3(kbcde[changed_verts].co, kbco[a]);
+			kbcde[changed_verts].vertex_index = a;
+			++changed_verts;
+		}
+	}
+
+	/* time to decide if we're going to win space by saving to compressed format */
+	if (changed_verts * sizeof(KB_ComprMeshDataEnt) < rk->totelem * sizeof(float) * 3) {
+		/*       size we get with compress      */   /* size we get without compress */
+		kb->compressed = 1;
+		kb->totelem = changed_verts;
+
+		MEM_freeN(kb->data);
+		kb->data = kbcde;
+
+		printf("Packed keyblock %s to %d verts\n", kb->name, changed_verts);
+	}
+	else {
+		MEM_freeN(kbcde);
+		/* just ensure */
+		kb->compressed = 0;
+	}
+}
+
+static void compress_keyblocks(Key *k)
+{
+	KeyBlock *kb = k->block.first;
+	while (kb) {
+		if (kb != k->refkey) {
+			compress_kb(kb, k);
+		}
+		kb = kb->next;
+	}
+}
+
 static void write_keys(WriteData *wd, ListBase *idbase)
 {
 	Key *key;
 	KeyBlock *kb;
 
-	key= idbase->first;
+	key = idbase->first;
+
 	while (key) {
 		if (key->id.us>0 || wd->current) {
 			/* write LibData */
@@ -1588,18 +1646,40 @@ static void write_keys(WriteData *wd, ListBase *idbase)
 			if (key->id.properties) IDP_WriteProperty(key->id.properties, wd);
 			
 			if (key->adt) write_animdata(wd, key->adt);
-			
+
 			/* direct data */
-			kb= key->block.first;
-			while (kb) {
-				writestruct(wd, DATA, "KeyBlock", 1, kb);
-				if (kb->data) writedata(wd, DATA, kb->totelem*key->elemsize, kb->data);
-				kb= kb->next;
+			if (GS(key->from->name) == ID_ME && !(U.flag & USER_LEGACY_KEYBLOCKS_FMT)) {
+				/* if mesh keys, save a compressed copy */
+				Key *dupe = BKE_key_copy_nolib(key);
+				compress_keyblocks(dupe);
+
+				kb = dupe->block.first;
+				while (kb) {
+					writestruct(wd, DATA, "KeyBlock", 1, kb);
+					if (kb->compressed) 
+						writedata(wd, DATA, sizeof(KB_ComprMeshDataEnt) * kb->totelem, kb->data);
+					else
+						writedata(wd, DATA, kb->totelem * key->elemsize, kb->data);
+					kb = kb->next;
+				}
+
+				BKE_key_free_nolib(dupe);
+				MEM_freeN(dupe);
+			}
+			else
+			{
+				kb = key->block.first;
+				while (kb) {
+					writestruct(wd, DATA, "KeyBlock", 1, kb);
+					if (kb->data) writedata(wd, DATA, kb->totelem * key->elemsize, kb->data);
+					kb = kb->next;
+				}
 			}
 		}
+
 		if (key->scratch.data)
 			writedata(wd, DATA, key->scratch.origin->totelem * key->elemsize, key->scratch.data);
-		key= key->id.next;
+		key = key->id.next;
 	}
 	/* flush helps the compression for undo-save */
 	mywrite(wd, MYWRITE_FLUSH, 0);
diff --git a/source/blender/makesdna/DNA_key_types.h b/source/blender/makesdna/DNA_key_types.h
index d1bb794..66b1a09 100644
--- a/source/blender/makesdna/DNA_key_types.h
+++ b/source/blender/makesdna/DNA_key_types.h
@@ -42,6 +42,13 @@
 struct AnimData;
 struct Ipo;
 
+#
+#
+typedef struct KB_ComprMeshDataEnt {
+	int vertex_index;
+	float co[3];
+} KB_ComprMeshDataEnt;
+
 typedef struct KeyBlock {
 	struct KeyBlock *next, *prev;
 
@@ -51,7 +58,10 @@ typedef struct KeyBlock {
 	float curval;      /* influence (typically [0 - 1] but can be more), (Key->type == KEY_RELATIVE) only.*/
 
 	short type;        /* interpolation type (Key->type == KEY_NORMAL) only. */
-	short pad1;
+	short compressed;  /* for disk save; if 1, then they key's data is laid out as an array of
+					    * KB_ComprMeshDataEnt structs (total totelem).
+						* Mesh only. Does not do anything at 
+						* runtime */
 
 	short relative;    /* relative == 0 means first key is reference, otherwise the index of Key->blocks */
 	short flag;
diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h
index 4f5670d..4e3cde3 100644
--- a/source/blender/makesdna/DNA_userdef_types.h
+++ b/source/blender/makesdna/DNA_userdef_types.h
@@ -574,6 +574,7 @@ typedef enum eUserPref_Flag {
 	USER_NONEGFRAMES		= (1 << 24),
 	USER_TXT_TABSTOSPACES_DISABLE	= (1 << 25),
 	USER_TOOLTIPS_PYTHON    = (1 << 26),
+	USER_LEGACY_KEYBLOCKS_FMT = (1 << 27)
 } eUserPref_Flag;
 
 /* flag */
diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c
index 7c101fb..6e8004a 100644
--- a/source/blender/makesrna/intern/rna_userdef.c
+++ b/source/blender/makesrna/intern/rna_userdef.c
@@ -4227,6 +4227,10 @@ static void rna_def_userdef_filepaths(BlenderRNA *brna)
 	RNA_def_property_boolean_sdna(prop, NULL, "flag", USER_FILECOMPRESS);
 	RNA_def_property_ui_text(prop, "Compress File", "Enable file compression when saving .blend files");
 
+	prop = RNA_def_property(srna, "legacy_keyblocks", PROP_BOOLEAN, PROP_NONE);
+	RNA_def_property_boolean_sdna(prop, NULL, "flag", USER_LEGACY_KEYBLOCKS_FMT);
+	RNA_def_property_ui_text(prop, "Legacy Keyblocks", "Don't compress keyblocks so previous Blender versions will be able to open the file");
+
 	prop = RNA_def_property(srna, "use_load_ui", PROP_BOOLEAN, PROP_NONE);
 	RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", USER_FILENOUI);
 	RNA_def_property_ui_text(prop, "Load UI", "Load user interface setup when loading .blend files");




More information about the Bf-blender-cvs mailing list