2009年11月18日星期三
2009年5月23日星期六
autorig的maya前端
highend3d上的链接
http://highend3d.com/maya/downloads/mel_scripts/character/5753.html
这里是论文,MIT的phd啊
http://www.mit.edu/~ibaran/autorig/
linux和macosx:
highend3d上只提供了win版的,前端是python写的,不过需要autorig的执行文件。autorig的程序叫Pinocchio,源码可以从http://github.com/elrond79/Pinocchio/tree/master下载。makefile有点小问题,需要自己加tab。
http://highend3d.com/maya/downloads/mel_scripts/character/5753.html
这里是论文,MIT的phd啊
http://www.mit.edu/~ibaran/autorig/
linux和macosx:
highend3d上只提供了win版的,前端是python写的,不过需要autorig的执行文件。autorig的程序叫Pinocchio,源码可以从http://github.com/elrond79/Pinocchio/tree/master下载。makefile有点小问题,需要自己加tab。
2008年12月14日星期日
定制右键标记菜单
在dagMenuProc.mel的dagMenuProc过程定义了各种不同物体的右键标记菜单。
比如,在
if (($object == "CubeCompass"))
{
createViewCubeMenuItems($parent);
return;
}
的下面加入:
if(`attributeExists "extNode" $object`){
print ($object+" RMB menu proc calls!\n");
return;
}
重新加载dagMenuProc.mel,新建一个物体,增加一个属性:extNode,类别任意。
再这个物体上使用右键菜单时,就会输出OBJECT NAME RMB menu proc calls!
如果在$parent上加入菜单,就可以显示菜单了。
技术细节:
dagMenuProc并没有出现在文档中,更详细的信息也没法得到,从网上搜索到的信息表明rmb标记菜单是附加在dag属性上的。
Maya's dagMenuProc script controls Right-Mouse-Button clicking on objects in the model viewports.
比如,在
if (($object == "CubeCompass"))
{
createViewCubeMenuItems($parent);
return;
}
的下面加入:
if(`attributeExists "extNode" $object`){
print ($object+" RMB menu proc calls!\n");
return;
}
重新加载dagMenuProc.mel,新建一个物体,增加一个属性:extNode,类别任意。
再这个物体上使用右键菜单时,就会输出OBJECT NAME RMB menu proc calls!
如果在$parent上加入菜单,就可以显示菜单了。
技术细节:
dagMenuProc并没有出现在文档中,更详细的信息也没法得到,从网上搜索到的信息表明rmb标记菜单是附加在dag属性上的。
Maya's dagMenuProc script controls Right-Mouse-Button clicking on objects in the model viewports.
2008年12月12日星期五
boost::regex匹配序列文件名2
regex默认是perl的语法,但是一些细微的地方和perl的不尽相同。今天就在这上面吃大亏了。
std::string regstr2="("+name+")([0-9]+{"+ia+"})([.]"+ext+")";
boost::regex expression2(regstr2);
就这两句,编译的时候没有问题,一运行就出错,总是说表达式不合法。折腾了半晚上才发现[0-9]+外面要加()才行。
用reggy工具的时候就没有这个问题([0-9]+){3}和[0-9]+{3}是同样的效果,在boost里后者就是不合法的表达式。
std::string regstr2="("+name+")([0-9]+{"+ia+"})([.]"+ext+")";
boost::regex expression2(regstr2);
就这两句,编译的时候没有问题,一运行就出错,总是说表达式不合法。折腾了半晚上才发现[0-9]+外面要加()才行。
用reggy工具的时候就没有这个问题([0-9]+){3}和[0-9]+{3}是同样的效果,在boost里后者就是不合法的表达式。
boost::regex匹配序列文件名
#include
#include
int main() {
std::string regstr = "(^.*/)([^/]+?)([0-9]+)([.])([^./]+$)";
boost::regex expression(regstr);
std::string testString = "/Users/cobranail/Documents/helghast_sub_ver2.4_mesh006.jpg";
boost::smatch what;
std::string::const_iterator start = testString.begin();
std::string::const_iterator end = testString.end();
while( boost::regex_search(start, end, what, expression) )
{
std::string path(what[1].first, what[1].second);
std::string name(what[2].first, what[2].second);
std::string index(what[3].first, what[3].second);
std::string ext(what[5].first, what[5].second);
std::cout<< "path:" << path.c_str() << std::endl;
std::cout<< "name:" << name.c_str() << std::endl;
std::cout<< "index:" << index.c_str() << std::endl;
std::cout<< "ext:" < start = what[0].second;
}
return 0;
}
可以匹配的文件名:
filename.001.ext
filename001.ext
filename1.ext
file.name.001.ext
file.name001.ext
(^.*/) ---->path
([^/]+?) ---->file
([0-9]+) ---->index
([.]) ------->dot
([^./]+$) ---->ext
参考
#include
int main() {
std::string regstr = "(^.*/)([^/]+?)([0-9]+)([.])([^./]+$)";
boost::regex expression(regstr);
std::string testString = "/Users/cobranail/Documents/helghast_sub_ver2.4_mesh006.jpg";
boost::smatch what;
std::string::const_iterator start = testString.begin();
std::string::const_iterator end = testString.end();
while( boost::regex_search(start, end, what, expression) )
{
std::string path(what[1].first, what[1].second);
std::string name(what[2].first, what[2].second);
std::string index(what[3].first, what[3].second);
std::string ext(what[5].first, what[5].second);
std::cout<< "path:" << path.c_str() << std::endl;
std::cout<< "name:" << name.c_str() << std::endl;
std::cout<< "index:" << index.c_str() << std::endl;
std::cout<< "ext:" <
}
return 0;
}
可以匹配的文件名:
filename.001.ext
filename001.ext
filename1.ext
file.name.001.ext
file.name001.ext
(^.*/) ---->path
([^/]+?) ---->file
([0-9]+) ---->index
([.]) ------->dot
([^./]+$) ---->ext
参考
2008年8月6日星期三
umf converter
e-motek的umfconv的mac版里destination format那个下拉列表看起来无法选中,其实只是没有显示而已,按下去,拖动鼠标再放开,可以看到格式可以变化。当然不能输入文字的问题估计是无法解决了。
2008年7月4日星期五
2008年5月23日星期五
GSL笔记20080523
最近的一个程序要用到一些矩阵和向量的操作,主程序是用c写的,自己实现一些矩阵的操作倒也不算困难,但是有些运算就不太好实现了,而且质量也难以保证。在google上搜索了一番,于是就发现了GSL(GNU Scientific Library),GNU的好东西还真不少。
很快的翻了一下gsl的手册,写的比较详细,再配合它的例子,一般的使用应该不成问题。
照葫芦画瓢,写了一个求逆矩阵的程序,当然都是数值运算的,好在这次的程序没有符号运算。
#include
#include
int
main (void)
{
double a_data[] = { 0.3, 0, 0, 0,
0, 0.3, 0, 0,
0, 0, 0.3, 0,
0, 0, 0, 0.3 };
double b_data[] = { 1.0, 2.0, 3.0, 4.0 };
gsl_matrix_view m
= gsl_matrix_view_array(a_data, 4, 4);
gsl_vector_view b
= gsl_vector_view_array(b_data, 4);
gsl_vector *x = gsl_vector_alloc (4);
gsl_matrix *mi= gsl_matrix_alloc (4,4);
int s;
gsl_permutation * p = gsl_permutation_alloc (4);
gsl_linalg_LU_decomp (&m.matrix, p, &s);
gsl_linalg_LU_invert (&m.matrix, p, mi);
printf ("inv=\n");
gsl_matrix_fprintf (stdout, mi, "%f");
gsl_permutation_free (p);
return 0;
}
permutation没有查到准确的意思,从手册和例子中的用法猜测,意思大概是行列式前面的符号。
gsl中的线性代数部分在操作矩阵的时候,都是把矩阵分解成某种乘积形式,然后再做运算,上面程序用的是LU分解把m分解成一个下三角阵和上三角阵的乘积,之后再求逆。
很快的翻了一下gsl的手册,写的比较详细,再配合它的例子,一般的使用应该不成问题。
照葫芦画瓢,写了一个求逆矩阵的程序,当然都是数值运算的,好在这次的程序没有符号运算。
#include
#include
int
main (void)
{
double a_data[] = { 0.3, 0, 0, 0,
0, 0.3, 0, 0,
0, 0, 0.3, 0,
0, 0, 0, 0.3 };
double b_data[] = { 1.0, 2.0, 3.0, 4.0 };
gsl_matrix_view m
= gsl_matrix_view_array(a_data, 4, 4);
gsl_vector_view b
= gsl_vector_view_array(b_data, 4);
gsl_vector *x = gsl_vector_alloc (4);
gsl_matrix *mi= gsl_matrix_alloc (4,4);
int s;
gsl_permutation * p = gsl_permutation_alloc (4);
gsl_linalg_LU_decomp (&m.matrix, p, &s);
gsl_linalg_LU_invert (&m.matrix, p, mi);
printf ("inv=\n");
gsl_matrix_fprintf (stdout, mi, "%f");
gsl_permutation_free (p);
return 0;
}
permutation没有查到准确的意思,从手册和例子中的用法猜测,意思大概是行列式前面的符号。
gsl中的线性代数部分在操作矩阵的时候,都是把矩阵分解成某种乘积形式,然后再做运算,上面程序用的是LU分解把m分解成一个下三角阵和上三角阵的乘积,之后再求逆。
2008年5月10日星期六
ADT, queue.h
以前写C的代码的时候,所有的ADT都是自己去实现,那真是一个累啊,无奈自己能力有限,要花费大量的时间来调试ADT部分的代码。终于,某天,在某论坛,看到某高人的一句话…解放了,直接用bsd的queue.h就好了。
能用的还有linux的list.h,不过我在osx上测试没有成功,总是说指针类型不匹配,就算是在linux上也要修改一下,里面已经说了,"don't include kernel headers in userspace"。
2008年5月2日星期五
Duncan提供的几个nCloth的tips
Here are a few points on breakable nCloth:
-If you select verts, edges or faces when making a tearable constraint then it can only tear at those regions, which is more efficient.
-The detach component code internally detachs based on vertices, not edges, so the tearable surface constraint will give "ragged" edges. You can make it more efficient by carefully modelling your own detach edges. Basically initially create your model with all the edges you wish to be breakable being border edges.
-For large rigid chunks you may wish to use separate objects, each with its own nCloth. Constrain the chunks together with a component to component constraint with glueStrength set and the weld method. Then combine, merge vertex, smooth normals and possibly extrude downstream of the nCloth nodes. This allows you to use rigidity on the nCloth nodes(otherwise you need to use high bend/stretch levels which is less efficient). You can also turn off self collision on the nCloth nodes, because they are internally rigid. If instead you were using one cloth node for all the chunks then you would need self collision so that the chunks could intercollide.
-There is bend stiffness on the constraint node, which can be used for glass shatter sorts of effects. However this requires that you have edge nComponents. If you use standard breakable surface setup with edge components it gets extremely slow to use the constraint bend stiffness for a complex surface. We are currently looking at fixing this problem, as it makes it hard to do rigid shatter effects.
Duncan
-If you select verts, edges or faces when making a tearable constraint then it can only tear at those regions, which is more efficient.
-The detach component code internally detachs based on vertices, not edges, so the tearable surface constraint will give "ragged" edges. You can make it more efficient by carefully modelling your own detach edges. Basically initially create your model with all the edges you wish to be breakable being border edges.
-For large rigid chunks you may wish to use separate objects, each with its own nCloth. Constrain the chunks together with a component to component constraint with glueStrength set and the weld method. Then combine, merge vertex, smooth normals and possibly extrude downstream of the nCloth nodes. This allows you to use rigidity on the nCloth nodes(otherwise you need to use high bend/stretch levels which is less efficient). You can also turn off self collision on the nCloth nodes, because they are internally rigid. If instead you were using one cloth node for all the chunks then you would need self collision so that the chunks could intercollide.
-There is bend stiffness on the constraint node, which can be used for glass shatter sorts of effects. However this requires that you have edge nComponents. If you use standard breakable surface setup with edge components it gets extremely slow to use the constraint bend stiffness for a complex surface. We are currently looking at fixing this problem, as it makes it hard to do rigid shatter effects.
Duncan
2008年4月28日星期一
关于mr framebuffer输出openEXR中的通道
openEXR是ILM设计的一种图像格式,可以比较灵活的存储图像数据,最近的这个demoreel中原始的图像序列都用exr了,唯一的缺点就是体积太大,一张640*360的rgba 4*32bit的图竟然要3.5MB的空间。
这里要说的是exr的另一个问题,在maya中用输出exr格式的图像时,sw渲染器不支持exr,这里说的是mr。如果在mr的framebuffer中设置成输入rgba,那么在生成的exr文件中,rgba通道都会用A B G R的名字表示。但是,如果要单独输出motion或者alpha的话,通道的名字就成了mi_buffer_###,###就是每一帧的序列号,也就是说每一帧的通道名称都是不一样的。
这样,在合成的时候就有点麻烦了。nuke的reader节点可以正常的预览的这种exr序列,创建reader节点之后,它是按通道来读取exr序列的,每都一帧,都会创建一个新的通道,可viewer节点却是按照通道来显示的,还有channel组里的节点也都是如此,这样一来每一帧改变一次通道。
这个问题解决起来也简单,只要该一下exr文件通道的名字即可,以framebuffer输出motion向量为例。

用一个16进制编辑器打开一个exr文件,可以看到从offset:0x1CL开始,就是通道和分量的名字,这里我们只需要修改0x26L开始的帧编号,只要把整个exr序列中的帧编号都替换成一个固定的字符串即可。我写了一个简单的程序来完成这个工作。
#include
#include
int main(int argc, char *argv[]){
char *str="mt0";
FILE *fp;
fp=fopen(argv[1],"rb+");
fseek(fp,0x26L,0);
fwrite(str,sizeof(char),3,fp);
fseek(fp,0x46L,0);
fwrite(str,sizeof(char),3,fp);
fseek(fp,0x66L,0);
fwrite(str,sizeof(char),3,fp);
fclose(fp);
return 0;
}
下面是修改后的exr文件头,我把帧编号都改成了mt0。
这里要说的是exr的另一个问题,在maya中用输出exr格式的图像时,sw渲染器不支持exr,这里说的是mr。如果在mr的framebuffer中设置成输入rgba,那么在生成的exr文件中,rgba通道都会用A B G R的名字表示。但是,如果要单独输出motion或者alpha的话,通道的名字就成了mi_buffer_###,###就是每一帧的序列号,也就是说每一帧的通道名称都是不一样的。
这样,在合成的时候就有点麻烦了。nuke的reader节点可以正常的预览的这种exr序列,创建reader节点之后,它是按通道来读取exr序列的,每都一帧,都会创建一个新的通道,可viewer节点却是按照通道来显示的,还有channel组里的节点也都是如此,这样一来每一帧改变一次通道。
这个问题解决起来也简单,只要该一下exr文件通道的名字即可,以framebuffer输出motion向量为例。

用一个16进制编辑器打开一个exr文件,可以看到从offset:0x1CL开始,就是通道和分量的名字,这里我们只需要修改0x26L开始的帧编号,只要把整个exr序列中的帧编号都替换成一个固定的字符串即可。我写了一个简单的程序来完成这个工作。
#include
#include
int main(int argc, char *argv[]){
char *str="mt0";
FILE *fp;
fp=fopen(argv[1],"rb+");
fseek(fp,0x26L,0);
fwrite(str,sizeof(char),3,fp);
fseek(fp,0x46L,0);
fwrite(str,sizeof(char),3,fp);
fseek(fp,0x66L,0);
fwrite(str,sizeof(char),3,fp);
fclose(fp);
return 0;
}
下面是修改后的exr文件头,我把帧编号都改成了mt0。
2008年2月13日星期三
maya fluid cache笔记1
FOR4 CACH <------------ This exists for all disk cache
. STIM startTime (TanimTime)
. ETIM endTime (TanimTime)
. TYPE isOverSampling (Tboolean)
. RATE samplingRate (TuInt32)
// block #1
FOR4 FLUD
.TIME currentTime (TanimTime)
.WRES widthRes (Tint32)
.HRES heightRes (Tint32)
.DRES depthRes (Tint32)
.NUMD numDensVal (Tint32)
.DENS densities (Tfloats)
.NMVX numVelX (Tint32)
.NMVY numVelY (Tint32)
.NMVZ numVelZ (Tint32)
.VELX xVelocities (Tfloats)
.VELY yVelocities (Tfloats)
.VELZ zVelocities (Tfloats)
.NUMT numTemperature (Tinit32)
.TEMP temperatures (Tfloats)
.NUMR numReaction (Tinit32)
.REAC reactions (Tfloats)
.NUMC numColors (Tint32)
.COLR RedColors (Tfloats)
.COLB BlueColors (Tfloats)
.COLG GreenColors (Tfloats)
.NUMU numTexCoord (Tint32)
.TEXU uCoords (Tfloats)
.TEXV vCoords (Tfloats)
.TEXW wCoords (Tfloats)
// block #2
FOR4 FLUD
........
Note:
TanimTime: an unsigned int, units are 1/6000 second
Tfloats: an array of floats, the count is specified by the
preceding "num" tag.
以上引用自maya文档。
下面是一个4x4的2d流体的cache,只有density部分。

用STIM作为例子来说明,53 54 49 4D 00 00 00 04,是STIM段的标识,而后面的00 00 00 FA才是SITM段的数据。00 00 00 FA是一个无符号整数,作为一个整数读入之后,是FA 00 00 00的顺序,这与本意是相反的(开始我以为是mac才这样,后来发现在linux上也是如此)。在读入之后,需要直接通过内存地址修改数据的顺序。
后面的DENS数据段里的42 C8 00 00,在内存中,应该是(低地址)00 00 C8 42(高地址)的顺序。
2008年1月15日星期二
Dryad
http://dryad.stanford.edu/index.php
当斯坦福大学计算机科学家Vladlen Koltun计划建立一个更完美的虚拟世界时,他选择了树——数以百万计,现在他想把它们送给别人种植。
ps:输出的obj有问题,不如在软件中的好看,maya打开obj后出错,blender打开后树的模型很粗糙。
2007年12月20日星期四
数组与指针---都是"退化"惹的祸
转自:http://bbs.chinaunix.net/thread-1031622-1-1.html
个人的浅显认识, 欢迎批评指正.
1. 什么是数组类型?
下面是C99中原话:
An array type describes a contiguously allocated nonempty set of objects with a
particular member object type, called the element type.36) Array types are characterized by their element type and by the number of elements in the array. An array type is said to be derived from its element type, and if its element type is T , the array type is sometimes called ‘‘array of T ’’. The construction of an array type from an element type is called ‘‘array type derivation’’.
很显然, 数组类型也是一种数据类型, 其本质功能和其他类型无异:定义该类型的数据所占内存空间的大小以及可以对该类型数据进行的操作(及如何操作).
2. 数组类型定义的数据是什么?它是变量还是常量?
char s[10] = "china";
在这个例子中, 数组类型为 array of 10 chars(姑且这样写), 定义的数据显然是一个数组s.
下面是C99中原话:
An lvalue is an expression with an object type or an incomplete type other than void; if an lvalue does not designate an object when it is evaluated, the behavior is undefined. When an object is said to have a particular type, the type is specified by the lvalue used to designate the object. A modifiable lvalue is an lvalue that does not have array type, does not have an incomplete type, does not have a const-qualified type, and if it is a structure or union, does not have any member (including, recursively, any member or element of all contained aggregates or unions) with a const-qualified type.
看了上面的定义, 大家应该明白了modifiable lvalue和lvalue的区别, 大家也应该注意到array type定义的是lvalue而不是modifiable lvalue.所以说s是lvalue.
s指代的是整个数组, s的内容显然是指整个数组中的数据, 它是china\0****(这里*表示任意别的字符).s的内容是可以改变的, 从这个意义上来说, s显然是个变量.
3. 数组什么时候会"退化"
下面是C99中原话:
Except when it is the operand of the sizeof operator or the unary & operator, or is a string literal used to initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue.
上面这句话说的很清楚了, 数组在除了3种情况外, 其他时候都要"退化"成指向首元素的指针.
比如对 char s[10] = "china";
这3中例外情况是:
(1) sizeof(s)
(2) &s;
(3) 用来初始化s的"china";
除了上述3种情况外,s都会退化成&s[0], 这就是数组变量的操作方式
4. 数组与指针有什么不同?
4.1 初始化的不同
char s[] = "china";
char *p = "china";
在第一句中,以&s[0]开始的连续6个字节内存分别被赋值为:
'c', 'h', 'i', 'n', 'a', '\0'
第二句中,p被初始化为程序data段的某个地址,该地址是字符串"china"的首地址
4.2 sizeof的不同
sizeof就是要求一种数据(类型)所占内存的字节数. 对于4.1中的s和p
sizeof(s)应为6, 而sizeof(p)应为一个"指针"的大小.
这个结果可以从1中对于数组类型的定义和3中数组什么时候不会"退化"中得出来.
4.3 &操作符
对于&操作符, 数组同样不会退化.
4.1中的s和p分别取地址后,其意义为:
&s的类型为pointer to array of 6 chars.
&p的类型为pointer to pointer to char.
4.4 s退化后为什么不可修改
除3种情况外,数组s在表达式中都会退化为"指向数组首元素的指针", 既&s[0]
举个例子
int a;
(&a)++; //你想对谁++? 这显然是不对的
对(&s[0])++操作犹如(&a)++, 同样是不对的,这就导致退化后的s变成不可修改的了.
4.5 二维数组与二级指针
char s[10];与char *p;
char s2[10][8];与char **p2;
s与p的关系,s2与p2的关系,两者相同吗?
紧扣定义的时候又到了.
除3种情况外,数组在表达式中都会退化为"指向数组首元素的指针".
s退化后称为&s[0], 类型为pointer to char, 与p相同
s2退化后称为&s2[0], 类型为pointer to array of 8 chars, 与p2不同
4.6 数组作为函数参数
毫无疑问, 数组还是会退化.
void func(char s[10]); <===> void func(char *s);
void func(char s[10][8]); <===> void func(char (*s)[8]);
4.7 在一个文件中定义char s[8], 在另外一个文件中声明extern char *s. 这样可以吗?
---------file1.c---------
char s[8];
---------file2.c---------
extern char *s;
答案是不可以. 一般来说,在file2.c中使用*s会引起core dump, 这是为什么呢?
先考虑int的例子.
---------file1.c---------
int a;
---------file2.c---------
extern int a;
file1.c和file2.c经过编译后, 在file2.o的符号表中, a的地址是尚未解析的
file1.o和file2.o在链接后, file2.o中a的地址被确定.假设此地址为0xbf8eafae
file2.o中对该地址的使用,完全是按照声明extern int a;进行的,即0xbf8eafae会被认为是整形a的地址
比如 a = 2; 其伪代码会对应为 *((int *)0xbf8eafae) = 2;
现在再看原来的例子.
---------file1.c---------
char s[8];
---------file2.c---------
extern char *s;
同样, file1.c和file2.c经过编译后, 在file2.o的符号表中, s的地址是尚未解析的
file1.o和file2.o在链接后, file2.o中s的地址被确定.假设此地址为0xbf8eafae
file2.o中对该地址的使用,完全是按照声明extern char *s;进行的,即0xbf8eafae会被认为是指针s的地址
比如 *s = 2; 其伪代码会对应为 *(*((char **)0xbf8eafae)) = 2;
*((char **)0xbf8eafae)会是什么结果呢?
这个操作的意思是:将0xbf8eafae做为一个二级字符指针, 将0xbf8eafae为始址的4个字节(32位机)作为一级字符指针
也就是将file1.o中的s[0], s[1], s[2], s[3]拼接成一个字符指针.
那么*(*((char **)0xbf8eafae)) = 2;的结果就是对file1.o中s[0], s[1], s[2], s[3]拼接成的这个地址对应
的内存赋值为2.
这样怎么会正确呢?
下面看看正确的写法:
---------file1.c---------
char s[8];
---------file2.c---------
extern char s[];
同样, file1.c和file2.c经过编译后, 在file2.o的符号表中, s的地址是尚未解析的
file1.o和file2.o在链接后, file2.o中s的地址被确定.假设此地址为0xbf8eafae
file2.o中对该地址的使用,完全是按照声明extern char s[];进行的,即0xbf8eafae会被认为是数组s的地址
比如 *s = 2; 其伪代码会对应为 *(*((char (*)[])0xbf8eafae)) = 2;
*((char (*)[])0xbf8eafae)会是什么结果呢?
这个操作的意思是:将0xbf8eafae做为一个指向字符数组的指针, 然后对该指针进行*操作.
这就用到了数组的一个重要性质:
对于数组 char aaa[10];来说, &aaa[0], &aaa, *(&aaa)在数值上是相同的(其实, *(&aaa)之所以在程序中
会在值上等于&aaa[0], 这也是退化的结果: *(&aaa)就是数组名aaa, aaa退化为&aaa[0]).
所以, *((char (*)[])0xbf8eafae)的结果在值上还是0xbf8eafae, 在类型上退化成"指向数组首元素的指针"
那么*(*((char (*)[])0xbf8eafae)) = 2;
其伪代码就成为*((char *)0xbf8eafae) = 2; 即将数组s的第一个元素设为2
5. 小结论
(a). 数组类型是一种特殊类型, 它定义的是数组变量, 是lvalue但不是modifiable lvalue
(b). 除了3种情况外(sizeof, &, 用做数组初始化的字符串数组), 数组会退化成"指向数组首元素的指针"
(c). 不要将数组名简单的看作不可修改的相应的指针, 它们还是有很多不同的
个人的浅显认识, 欢迎批评指正.
1. 什么是数组类型?
下面是C99中原话:
An array type describes a contiguously allocated nonempty set of objects with a
particular member object type, called the element type.36) Array types are characterized by their element type and by the number of elements in the array. An array type is said to be derived from its element type, and if its element type is T , the array type is sometimes called ‘‘array of T ’’. The construction of an array type from an element type is called ‘‘array type derivation’’.
很显然, 数组类型也是一种数据类型, 其本质功能和其他类型无异:定义该类型的数据所占内存空间的大小以及可以对该类型数据进行的操作(及如何操作).
2. 数组类型定义的数据是什么?它是变量还是常量?
char s[10] = "china";
在这个例子中, 数组类型为 array of 10 chars(姑且这样写), 定义的数据显然是一个数组s.
下面是C99中原话:
An lvalue is an expression with an object type or an incomplete type other than void; if an lvalue does not designate an object when it is evaluated, the behavior is undefined. When an object is said to have a particular type, the type is specified by the lvalue used to designate the object. A modifiable lvalue is an lvalue that does not have array type, does not have an incomplete type, does not have a const-qualified type, and if it is a structure or union, does not have any member (including, recursively, any member or element of all contained aggregates or unions) with a const-qualified type.
看了上面的定义, 大家应该明白了modifiable lvalue和lvalue的区别, 大家也应该注意到array type定义的是lvalue而不是modifiable lvalue.所以说s是lvalue.
s指代的是整个数组, s的内容显然是指整个数组中的数据, 它是china\0****(这里*表示任意别的字符).s的内容是可以改变的, 从这个意义上来说, s显然是个变量.
3. 数组什么时候会"退化"
下面是C99中原话:
Except when it is the operand of the sizeof operator or the unary & operator, or is a string literal used to initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue.
上面这句话说的很清楚了, 数组在除了3种情况外, 其他时候都要"退化"成指向首元素的指针.
比如对 char s[10] = "china";
这3中例外情况是:
(1) sizeof(s)
(2) &s;
(3) 用来初始化s的"china";
除了上述3种情况外,s都会退化成&s[0], 这就是数组变量的操作方式
4. 数组与指针有什么不同?
4.1 初始化的不同
char s[] = "china";
char *p = "china";
在第一句中,以&s[0]开始的连续6个字节内存分别被赋值为:
'c', 'h', 'i', 'n', 'a', '\0'
第二句中,p被初始化为程序data段的某个地址,该地址是字符串"china"的首地址
4.2 sizeof的不同
sizeof就是要求一种数据(类型)所占内存的字节数. 对于4.1中的s和p
sizeof(s)应为6, 而sizeof(p)应为一个"指针"的大小.
这个结果可以从1中对于数组类型的定义和3中数组什么时候不会"退化"中得出来.
4.3 &操作符
对于&操作符, 数组同样不会退化.
4.1中的s和p分别取地址后,其意义为:
&s的类型为pointer to array of 6 chars.
&p的类型为pointer to pointer to char.
4.4 s退化后为什么不可修改
除3种情况外,数组s在表达式中都会退化为"指向数组首元素的指针", 既&s[0]
举个例子
int a;
(&a)++; //你想对谁++? 这显然是不对的
对(&s[0])++操作犹如(&a)++, 同样是不对的,这就导致退化后的s变成不可修改的了.
4.5 二维数组与二级指针
char s[10];与char *p;
char s2[10][8];与char **p2;
s与p的关系,s2与p2的关系,两者相同吗?
紧扣定义的时候又到了.
除3种情况外,数组在表达式中都会退化为"指向数组首元素的指针".
s退化后称为&s[0], 类型为pointer to char, 与p相同
s2退化后称为&s2[0], 类型为pointer to array of 8 chars, 与p2不同
4.6 数组作为函数参数
毫无疑问, 数组还是会退化.
void func(char s[10]); <===> void func(char *s);
void func(char s[10][8]); <===> void func(char (*s)[8]);
4.7 在一个文件中定义char s[8], 在另外一个文件中声明extern char *s. 这样可以吗?
---------file1.c---------
char s[8];
---------file2.c---------
extern char *s;
答案是不可以. 一般来说,在file2.c中使用*s会引起core dump, 这是为什么呢?
先考虑int的例子.
---------file1.c---------
int a;
---------file2.c---------
extern int a;
file1.c和file2.c经过编译后, 在file2.o的符号表中, a的地址是尚未解析的
file1.o和file2.o在链接后, file2.o中a的地址被确定.假设此地址为0xbf8eafae
file2.o中对该地址的使用,完全是按照声明extern int a;进行的,即0xbf8eafae会被认为是整形a的地址
比如 a = 2; 其伪代码会对应为 *((int *)0xbf8eafae) = 2;
现在再看原来的例子.
---------file1.c---------
char s[8];
---------file2.c---------
extern char *s;
同样, file1.c和file2.c经过编译后, 在file2.o的符号表中, s的地址是尚未解析的
file1.o和file2.o在链接后, file2.o中s的地址被确定.假设此地址为0xbf8eafae
file2.o中对该地址的使用,完全是按照声明extern char *s;进行的,即0xbf8eafae会被认为是指针s的地址
比如 *s = 2; 其伪代码会对应为 *(*((char **)0xbf8eafae)) = 2;
*((char **)0xbf8eafae)会是什么结果呢?
这个操作的意思是:将0xbf8eafae做为一个二级字符指针, 将0xbf8eafae为始址的4个字节(32位机)作为一级字符指针
也就是将file1.o中的s[0], s[1], s[2], s[3]拼接成一个字符指针.
那么*(*((char **)0xbf8eafae)) = 2;的结果就是对file1.o中s[0], s[1], s[2], s[3]拼接成的这个地址对应
的内存赋值为2.
这样怎么会正确呢?
下面看看正确的写法:
---------file1.c---------
char s[8];
---------file2.c---------
extern char s[];
同样, file1.c和file2.c经过编译后, 在file2.o的符号表中, s的地址是尚未解析的
file1.o和file2.o在链接后, file2.o中s的地址被确定.假设此地址为0xbf8eafae
file2.o中对该地址的使用,完全是按照声明extern char s[];进行的,即0xbf8eafae会被认为是数组s的地址
比如 *s = 2; 其伪代码会对应为 *(*((char (*)[])0xbf8eafae)) = 2;
*((char (*)[])0xbf8eafae)会是什么结果呢?
这个操作的意思是:将0xbf8eafae做为一个指向字符数组的指针, 然后对该指针进行*操作.
这就用到了数组的一个重要性质:
对于数组 char aaa[10];来说, &aaa[0], &aaa, *(&aaa)在数值上是相同的(其实, *(&aaa)之所以在程序中
会在值上等于&aaa[0], 这也是退化的结果: *(&aaa)就是数组名aaa, aaa退化为&aaa[0]).
所以, *((char (*)[])0xbf8eafae)的结果在值上还是0xbf8eafae, 在类型上退化成"指向数组首元素的指针"
那么*(*((char (*)[])0xbf8eafae)) = 2;
其伪代码就成为*((char *)0xbf8eafae) = 2; 即将数组s的第一个元素设为2
5. 小结论
(a). 数组类型是一种特殊类型, 它定义的是数组变量, 是lvalue但不是modifiable lvalue
(b). 除了3种情况外(sizeof, &, 用做数组初始化的字符串数组), 数组会退化成"指向数组首元素的指针"
(c). 不要将数组名简单的看作不可修改的相应的指针, 它们还是有很多不同的
2007年12月14日星期五
Maya 2008 API Template for Xcode
http://www.thomascraigen.com/tools/index.html
开发maya插件的xcode模板,不多说了。
开发maya插件的xcode模板,不多说了。
2007年12月12日星期三
不同拓扑结构的模型之间的变换1
cmiVFX的免费教程
最近cmi有提供了一篇免费的blender教程,主要说的是用node来做合成。用blender做合成,看起来很有nuke的感觉,只是功能和稳定性比起nuke差些。
http://cgchannel.com/news/viewfeature.jsp?newsid=6763
现在的blender真是一个怪物,各个方面都可以做,3D、后期、剪辑通吃,只是做的都不是很好。
差点忘了,cmi的网站要穿墙才能访问到。
http://cgchannel.com/news/viewfeature.jsp?newsid=6763
现在的blender真是一个怪物,各个方面都可以做,3D、后期、剪辑通吃,只是做的都不是很好。
差点忘了,cmi的网站要穿墙才能访问到。
Automatic Rigging and Animation of 3D Characters
http://www.mit.edu/~ibaran/autorig/
以前曾经考虑过自动装配角色的问题,现在已经有人做出来了。大概看了一下paper和视频,对于复杂的模型,这样rig恐怕还不行,不过动画转换的功能却非常不错。
2007年12月3日星期一
TopMod-拓扑建模软件

在blender的论坛里冒泡的时候,无意中发现了这个软件。是一个google code上的项目,基于QT4,opensource,支持osx和windows,貌似linux也在支持之列。下载之后试了试,不会用。
http://www.topmod3d.org
订阅:
博文 (Atom)

