基于HT的CSG功能构建HTML5的3D书架

简介: 构造实体几何CSG全称Constructive solid geometry,是3D计算机图形学中构建模型的常用技术,可通过合并Union、相减Subtraction和相交Intersction的三种取集的逻辑运算,将立方体、圆柱体和棱柱等简单的基础模型,嵌套组合成更复杂三维模型。

构造实体几何CSG全称Constructive solid geometry,是3D计算机图形学中构建模型的常用技术,可通过合并Union、相减Subtraction和相交Intersction的三种取集的逻辑运算,将立方体、圆柱体和棱柱等简单的基础模型,嵌套组合成更复杂三维模型。

CSG的算法这些年来已有各种语言平台版本实现,C++版主流的是 http://opencsg.org/ 已有众多基于该开源类库的应用案例,JavaScript语言较早版实现 http://evanw.github.io/csg.js/ 影响较广,很多其他js衍生版都是基于该版本进行改进完善,包括Java版的实现 https://github.com/miho/JCSG ,可参考基于JavaFX的3D打印IDE https://github.com/miho/JFXScad ,提起JavaFX视乎这些年完全消失在程序员视野毫无声息,但还是有一群拥护者持续在使用着如今地位有点尴尬的JavaFX。

Screen Shot 2014-12-25 at 12.19.17 AM

回到我们今天要搞的3D书架例子,我们将基于HT for Web的3D引擎来实现,HT已经内置了CSG功能的模型封装,我们通过构建CSGNode图元对象,该类型图元可对Host吸附的图元进行CSG技术的合集、并集和补集的三种操作,一般运用中裁剪的方式较为常用,因此CSGNode默认对Host图元的操作就是裁剪。

上图的例子效果可看出我们构建了一个DataModel数据模型,该模型绑定了一个TreeView树组件和两个Graph3dView的三维组件,上部分的Graph3dView组件添加了VisibleFunc的可见过滤器,隐藏了如下部分的Graph3dView中蓝色立方体图元,这些蓝色立方体图元就是CSGNode,其作用就是用来裁剪其吸附的书架Shelf对象,因此一般在3D编辑器状态下才需要出现,运行时科如上部分Graph3dView组件那样,通过添加可见过滤器将其隐藏,这样就实现了有凹槽可摆放书籍内容的3D书架效果,本例我们作为示例仅放了一本《CSS3  The Missing Manual》,这本书其实是由一个六面体,front面显示了书籍贴图,然后旋转一定角度进行摆放,btw《CSS3  The Missing Manual》第三版是本很不错的CSS书籍,强烈推荐!

Screen Shot 2014-12-25 at 12.22.57 AM

书架两边分别摆放了两个不同风格的小书台,通过上图我拖拽改变了蓝色CSGNode图元的位置,大家通过两张图的对比能更直观的体会到CSG的操作效果,玻璃门开关以及相册效果都是直接利用HT for Web的3D引擎提供的模型,通过设置透明度、相片贴图和旋转动画等呢只功能参数即可。

以下是该HT for Web的3D例子的所有JavaScript代码供参考:http://v.youku.com/v_show/id_XODU2MTQ4NjI4.html

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);
                  
}   

 

相关文章
HTML5 SVG实现可爱的小鸟卡通动画3D特效
HTML5 SVG实现可爱的小鸟卡通动画
64 29
HTML基础知识:构建网页的基石
【10月更文挑战第11天】HTML基础知识:构建网页的基石
324 0
html5 + Three.js 3D风雪封印在棱镜中的梅花鹿动效源码
html5 + Three.js 3D风雪封印在棱镜中的梅花鹿动效源码。画面中心是悬浮于空的梅花鹿,其四周由白色线段组成了一个6边形将中心的梅花鹿包裹其中。四周漂浮的白雪随着多边形的转动而同步旋转。建议使用支持HTML5与css3效果较好的火狐(Firefox)或谷歌(Chrome)等浏览器预览本源码。
105 2
HTML5 3D地球仪可按经纬坐标定位特效
这是一个基于HTML5的3D地球仪动画,地球仪不仅可以自动自西向东旋转,而且还可以旋转到指定经纬度坐标。另外,还有一个控制面板,可以控制地球是否自转、光晕是否显示,以及地理缩放。你也可以通过拖拽鼠标来改变地球仪的视角,可以将它移至南北极的视角,也可以移至赤道的视角,非常方便。需要的朋友可下载试试!
127 2
如何利用HTML和CSS构建企业级网站的全过程。从项目概述到页面结构设计,再到HTML结构搭建与CSS样式设计,最后实现具体页面并进行优化提升,全面覆盖了网站开发的关键步骤
本文深入介绍了如何利用HTML和CSS构建企业级网站的全过程。从项目概述到页面结构设计,再到HTML结构搭建与CSS样式设计,最后实现具体页面并进行优化提升,全面覆盖了网站开发的关键步骤。通过实例展示了主页、关于我们、产品展示、新闻动态及联系我们等页面的设计与实现,强调了合理布局、美观设计及用户体验的重要性。旨在为企业打造一个既专业又具吸引力的线上平台。
136 7
|
4月前
html页面点击按钮实现页面跳转功能
html页面点击按钮实现页面跳转
117 11
【HTML】构建网页的基石
本文介绍了HTML的基本概念和常用标签,包括HTML文件的基本结构、常见标签(如标题、段落、换行、图片、超链接等)、表格与表单的使用方法,以及无语义标签div和span的特性。通过具体示例展示了如何在VS Code中快速生成HTML框架及各标签的应用场景,帮助初学者快速掌握HTML的基础知识。
88 1
【HTML】构建网页的基石
HTML5 的 form 的自动完成功能
在HTML5中,`&lt;form&gt;`元素具备自动完成功能,可根据用户历史输入提供建议,提高输入效率并改善体验。默认情况下,浏览器会自动开启此功能,也可通过设置`autocomplete`属性为`on`或`off`来明确开启或关闭。对于特定表单字段,如`&lt;input&gt;`,同样可以通过设置`autocomplete`属性控制自动完成行为。浏览器通过记录并存储用户的历史输入,在用户再次访问相同表单时提供相应的自动完成建议。
HTML+JavaScript构建C++类代码一键转换MASM32代码平台
HTML+JavaScript构建C++类代码一键转换MASM32代码平台
|
6月前
|
C++
HTML+JavaScript构建一个将C/C++定义的ANSI字符串转换为MASM32定义的DWUniCode字符串的工具
HTML+JavaScript构建一个将C/C++定义的ANSI字符串转换为MASM32定义的DWUniCode字符串的工具
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等