构造实体几何全称,是3D计算机图形学中构建模型的常用技术,可通过合并Union、相减Subtraction和相交Intersction的三种取集的逻辑运算,将立方体、圆柱体和棱柱等简单的基础模型,嵌套组合成更复杂三维模型。
CSG的算法这些年来已有各种语言平台版本实现,C++版主流的是 已有众多基于该开源类库的应用案例,JavaScript语言较早版实现 影响较广,很多其他js衍生版都是基于该版本进行改进完善,包括Java版的实现 ,可参考基于JavaFX的3D打印IDE ,提起JavaFX视乎这些年完全消失在程序员视野毫无声息,但还是有一群拥护者持续在使用着如今地位有点尴尬的JavaFX。
回到我们今天要搞的3D书架例子,我们将基于的3D引擎来实现,已经内置了CSG功能的模型封装,我们通过构建CSGNode图元对象,该类型图元可对Host吸附的图元进行CSG技术的合集、并集和补集的三种操作,一般运用中裁剪的方式较为常用,因此CSGNode默认对Host图元的操作就是裁剪。
上图的例子效果可看出我们构建了一个数据模型,该模型绑定了一个树组件和两个的三维组件,上部分的Graph3dView组件添加了VisibleFunc的可见过滤器,隐藏了如下部分的Graph3dView中蓝色立方体图元,这些蓝色立方体图元就是CSGNode,其作用就是用来裁剪其吸附的书架Shelf对象,因此一般在3D编辑器状态下才需要出现,运行时科如上部分Graph3dView组件那样,通过添加可见过滤器将其隐藏,这样就实现了有凹槽可摆放书籍内容的3D书架效果,本例我们作为示例仅放了一本《CSS3 The Missing Manual》,这本书其实是由一个六面体,front面显示了书籍贴图,然后旋转一定角度进行摆放,btw《CSS3 The Missing Manual》第三版是本很不错的CSS书籍,强烈推荐!
书架两边分别摆放了两个不同风格的小书台,通过上图我拖拽改变了蓝色CSGNode图元的位置,大家通过两张图的对比能更直观的体会到CSG的操作效果,玻璃门开关以及相册效果都是直接利用的3D引擎提供的模型,通过设置透明度、相片贴图和旋转动画等呢只功能参数即可。
以下是该的3D例子的所有JavaScript代码供参考:
-
ht.Default.setImage('ben12', {
-
width: 100,
-
height: 50,
-
comps: [
-
{
-
type: 'image',
-
name: 'ben1',
-
rect: [0, 0, 50, 50]
-
},
-
{
-
type: 'image',
-
name: 'ben2',
-
rect: [50, 0, 50, 50]
-
}
-
]
-
});
-
-
function init(){
-
dm = new ht.DataModel();
-
treeView = new ht.widget.TreeView(dm);
-
gv1 = new ht.graph3d.Graph3dView(dm);
-
gv2 = new ht.graph3d.Graph3dView(dm);
-
splitView = new ht.widget.SplitView(gv1, gv2, 'v', 0.6);
-
mainSplit = new ht.widget.SplitView(treeView, splitView, 'h', 0.27);
-
-
view = mainSplit.getView();
-
view.className = 'main';
-
document.body.appendChild(view);
-
window.addEventListener('resize', function (e) {
-
mainSplit.invalidate();
-
}, false);
-
-
gv1.setMoveStep(30);
-
gv1.setGridVisible(true);
-
gv1.setEye(0, 100, 1000);
-
gv1.setCenter(0, 200, 0);
-
gv1.pan(0, 100, true);
-
gv1.getLabel = function(){
-
return null;
-
};
-
gv1.getBrightness = function(data){
-
return null;
-
};
-
gv1.setVisibleFunc(function(data){
-
if(data.showMe){
-
return true;
-
}
-
if(data instanceof ht.CSGNode && data.getHost()){
-
return false;
-
}
-
return true;
-
});
-
-
gv2.setMoveStep(30);
-
gv2.setEditable(true);
-
gv2.setGridVisible(true);
-
gv2.setEditable(true);
-
gv2.pan(0, 200, true);
-
gv2.getLabel = function(){
-
return null;
-
};
-
-
initShelf1();
-
initShelf2();
-
initShelf3();
-
-
treeView.expandAll();
-
-
var angle = 0;
-
setInterval(function(){
-
angle += Math.PI/40;
-
earth.r3(0, angle, 0);
-
photos.s('dw.angle', angle);
-
}, 50);
-
}
-
-
function initShelf1(){
-
var shelf = new ht.CSGNode();
-
shelf.s3(500, 400, 120);
-
shelf.p3(0, 200, 0);
-
shelf.setName('shelf1');
-
shelf.s({
-
'all.color': '#E5BB77'
-
});
-
dm.add(shelf);
-
-
for(var i=0; i<2; i++){
-
for(var j=0; j<5; j++){
-
var clipNode = new ht.CSGNode();
-
clipNode.setHost(shelf);
-
clipNode.s3(80, 100, 120);
-
clipNode.p3(-200+j*100, 340-i*120, 20);
-
clipNode.setName('substract-'+i+'-'+j);
-
clipNode.s('batch', 'tt');
-
clipNode.setParent(shelf);
-
dm.add(clipNode);
-
}
-
}
-
-
var leftNode = new ht.CSGNode();
-
leftNode.setHost(shelf);
-
leftNode.s3(23, 380, 100);
-
leftNode.p3(-255, 200, 0);
-
leftNode.setName('substract left');
-
leftNode.setParent(shelf);
-
dm.add(leftNode);
-
-
var rightNode = new ht.CSGNode();
-
rightNode.setHost(shelf);
-
rightNode.s3(23, 380, 100);
-
rightNode.p3(255, 200, 0);
-
rightNode.setName('substract right');
-
rightNode.setParent(shelf);
-
dm.add(rightNode);
-
-
var bottomNode = new ht.CSGNode();
-
bottomNode.setHost(shelf);
-
bottomNode.s3(480, 140, 140);
-
bottomNode.p3(0, 80, 0);
-
bottomNode.setName('substract bottom');
-
bottomNode.setParent(shelf);
-
dm.add(bottomNode);
-
-
var topNode = new ht.CSGNode();
-
topNode.setHost(shelf);
-
topNode.s3(480, 10, 100);
-
topNode.p3(0, 400, 0);
-
topNode.setName('union top');
-
topNode.s('attach.operation', 'union');
-
topNode.setParent(shelf);
-
dm.add(topNode);
-
-
var book = new ht.Node();
-
book.setName('CSS3: The Missing Manual');
-
book.s3(60, 80, 8);
-
book.p3(-100, 210, 20);
-
book.r3(-Math.PI/6, Math.PI/5, 0);
-
book.setIcon('book');
-
book.s({
-
'front.image': 'book',
-
'back.color': 'white',
-
'left.color': 'white',
-
'all.color': 'gray'
-
});
-
book.setHost(shelf);
-
book.setParent(shelf);
-
dm.add(book);
-
-
}
-
-
function initShelf2(){
-
var shelf = new ht.CSGNode();
-
shelf.s3(120, 240, 120);
-
shelf.p3(0, 120, 0);
-
shelf.setName('shelf2');
-
shelf.s({
-
'all.color': '#805642',
-
'csg.color': 'yellow',
-
'csg.reverse.flip': true
-
});
-
dm.add(shelf);
-
-
var clipNode = new ht.CSGNode();
-
clipNode.setName('shelf2-substract-up');
-
clipNode.s3(100, 100, 130);
-
clipNode.p3(0, 180, 0);
-
clipNode.setHost(shelf);
-
clipNode.s('attach.cull', true);
-
clipNode.setParent(shelf);
-
dm.add(clipNode);
-
-
clipNode = new ht.CSGBox();
-
clipNode.setName('CSGBox-Expand-Left');
-
clipNode.s3(100, 100, 120);
-
clipNode.p3(0, 65, 0.1);
-
clipNode.setHost(shelf);
-
clipNode.showMe = true;
-
clipNode.s({
-
'all.visible': false,
-
'front.visible': true,
-
'front.toggleable': true,
-
'front.reverse.flip': true,
-
'front.transparent': true,
-
'front.end': Math.PI * 0.7,
-
'front.color': 'rgba(0, 50, 50, 0.7)'
-
});
-
clipNode.setParent(shelf);
-
clipNode.setFaceExpanded('front', true, true);
-
dm.add(clipNode);
-
-
earth = new ht.Node();
-
earth.setName('earth');
-
earth.setIcon('earth');
-
earth.s3(70, 70, 70);
-
earth.p3(0, 50, 0);
-
earth.s({
-
'shape3d': 'sphere',
-
'shape3d.image': 'earth'
-
});
-
earth.setHost(shelf);
-
earth.setParent(shelf);
-
dm.add(earth);
-
-
shelf.t3(-360, 0, 50);
-
shelf.r3(0, Math.PI/7, 0);
-
}
-
-
function initShelf3(){
-
var shelf = new ht.CSGNode();
-
shelf.s3(120, 240, 120);
-
shelf.p3(0, 120, 0);
-
shelf.setName('shelf3');
-
shelf.setIcon('ben');
-
shelf.s({
-
'all.image': 'brick',
-
'all.uv.scale': [2, 4],
-
'top.uv.scale': [2, 2],
-
'bottom.uv.scale': [2, 2],
-
'csg.image': 'ben',
-
'csg.blend': 'yellow'
-
});
-
dm.add(shelf);
-
-
photos = new ht.DoorWindow();
-
photos.setName('DoorWindow-Photos');
-
photos.setIcon('ben12');
-
photos.s3(110, 100, 130);
-
photos.p3(5, 180, 0);
-
photos.setHost(shelf);
-
photos.showMe = true;
-
photos.s({
-
'bottom.uv': [1,1, 1,0, 0,0, 0,1],
-
'bottom.uv.scale': [1, 1],
-
'left.uv.scale': [3, 3],
-
'top.uv.scale': [2, 2],
-
'dw.s3': [0.8, 0.9, 0.05],
-
'dw.t3': [0, -5, 0],
-
'dw.axis': 'v',
-
'dw.toggleable': false,
-
'front.image': 'ben1',
-
'back.image': 'ben2',
-
'all.color': '#F8CE8B'
-
});
-
photos.setParent(shelf);
-
dm.add(photos);
-
-
var clipNode = new ht.CSGBox();
-
clipNode.setName('CSGBox-Expand-Top');
-
clipNode.s3(100, 100, 120);
-
clipNode.p3(0, 65, 0.1);
-
clipNode.setHost(shelf);
-
clipNode.showMe = true;
-
clipNode.s({
-
'all.visible': false,
-
'front.visible': true,
-
'front.color': 'red',
-
'front.transparent': true,
-
'front.opacity': 0.7,
-
'front.reverse.flip': true,
-
'front.toggleable': true,
-
'front.axis': 'top',
-
'front.end': Math.PI * 0.4
-
});
-
clipNode.setParent(shelf);
-
clipNode.setFaceExpanded('front', true, true);
-
dm.add(clipNode);
-
-
shelf.t3(360, 0, 50);
-
shelf.r3(0, -Math.PI/7, 0);
-
-
}
阅读(3008) | 评论(0) | 转发(0) |