Now, 3D models have been used in a variety of different fields. They are used in the medical industry to make accurate models of organs; the film industry uses them for moving characters, objects, and real-life movies; the video game industry uses them as resources in computers and video games; and the science industry uses them as accurate models of chemical compounds ; the construction industry uses them to present proposed representations of buildings or landscapes; the engineering community uses them to design new equipment, vehicles, structures, and other applications; in recent decades, the field of earth sciences has begun to build 3D geological models, Also, 3D models are often animated, for example, they are used extensively in feature films, as well as in computer and video games. They can be used in 3D modeling tools or alone. In order to easily form animation, some additional data is usually added to the model. For example, some 3D models of humans or animals have a complete skeletal system, so that the movement will look more realistic, and the movement can be controlled by joints and bones.
All of these make us front-end developers feel that if we can achieve 3D effects without learning unity3d or other game development tools, and can accurately control movement or direction by code. . . So I used the 3D components in HT For Web to implement a small example, using most of the functions of the 3D components in HT , and doing this example is to master the 3D components and try to put them into an example. You can refer to it if necessary.
Let's take a look at the overall effect diagram:
Using HT for Web , it is not a problem to create a three-layer backplane from the existing 3d template, the problem is how to put the "computer" and "cabinet components" on the first layer in the picture? I downloaded the obj format file from the Internet, and then I used the ht.Default.loadObj(objUrl, mtlUrl, params) function in HT to load the model, and the params part can refer to http://www.hightopo. com/guide/guide/plugin/obj/ht-obj-guide.html , the code is as follows:
ht.Default.loadObj('obj/cabinet assembly 1.obj', 'obj/cabinet assembly 1.mtl', { //Load obj file cube: true, //Whether to scale the model to the size range of unit 1, the default is false center: true, //Whether the model is centered, the default is false, setting it to true will move the model position to center its content shape3d: 'box', //If the shape3d name is specified, HT will automatically build all the parsed material models into an array and register with this name finishFunc: function(modelMap, array, rawS3){ //For callback processing after loading if(modelMap){ device2 = createNode('box', floor1); //Create a node on the first floor "floor" device2.p3([x1-120, y1+13, z1+60]); //Set the coordinates of this node device2.s3(rawS3); //Set the size of this node createEdge(device1, device2); //Create a connection device3 = createNode('box', floor1); device3.s3(rawS3); device3.p3([x1+120, y1+13, z1+60]); createEdge(device1, device3); } } });
The three parameters in the finishFunc function are defined as follows:
- modelMap: The return value after calling ht.Default.parseObj parsing, if the loading or parsing fails, the return value is empty
- array: an array of all material models
- rawS3: contains the original size of all models
Generally, in practical applications, we will set the size of the primitive to the original size of the model.
There is a red "warning" above the "computer" that the stereo can be rotated, which depends on the ht.Default.setShape3dModel function (HT for Web Modeling Manual http://www.hightopo.com/guide/guide/plugin/modeling/ht -modeling-guide.html ) registered a 3d model, in ht, there are many encapsulated modeling functions, the more basic ones are spheres, cylinders, cubes, etc. Here I use the method createRingModel to construct a ring Generate the outermost ring of "warning", the upper part of the exclamation mark is the sphere constructed with createSmoothSphereModel, and the lower part of the exclamation mark is the cylinder constructed with createSmoothCylinderModel. I directly used the functions encapsulated in the 3d model at first, so I didn't know what the parameters used in the function were used for, and I didn't understand how the 3d model was composed, and then I re-read the previous "" "Model foundation", only to know that a surface used in the original 3d model, the most basic is the triangular surface, and then the complex surface is also formed by multiple triangular surfaces, and then formed by rotating around a specific axis, of course, This axis is up to you. Different axes can generate different shapes. For style settings such as colors, please refer to the HT for Web Style Manual ( http://www.hightopo.com/guide/guide/core/theme/ ht-theme-guide.html ). As for how to rotate the 3d model, the addScheduleTask(Task) method is encapsulated in ht. I call a rotation function setRotation encapsulated in ht in the third-layer Task to set the order and direction of rotation, and specify the object to be rotated. Here's how to customize the "warning" 3d model (note: because the model in this example is a custom combination, if you want to set the color of the overall model, use the "all.blend" style property):
function createAlarm(device, formPane) { var ringModel = ht.Default.createRingModel([ 8, 1, 10, 1, 10, -1, 8, -1, 8, 1 ], null, null, false, false, 100);//According to the xy plane Curves, wrapping around to form a 3D model. var sphereModel = ht.Default.createSmoothSphereModel(8, 8, 0, Math.PI*2, 0, Math.PI, 2);//Build a smooth sphere model var cylinderModel = ht.Default.createSmoothCylinderModel(8, true, true, 1, 2, 0, Math.PI*2, 8);//Build a smooth cylinder model var alarmArr = [//The combined model is composed of three models ringModel, sphereModel, cylinderModel { shape3d: ringModel,//Define the model type r3: [Math.PI/2, 0, 0],//Set the rotation angle color: {//Set the model color func: '[email protected]',//The all.blend attribute in the data binding style style, which can be obtained and set through data.s() } },{ shape3d: sphereModel, t3: [0, 4, 0], color: { func: '[email protected]', } },{ shape3d: cylinderModel, t3: [0, -3, 0], color: { func: '[email protected]', } } ]; ht.Default.setShape3dModel('alarm', {//Register custom 3D model shape3d: alarmArr }); var alarmTip = createNode('alarm', device);//Create a node whose shape3d is alarm alarmTip.s3([2, 2, 2]);//Set the node size alarmTip.p3(device.p3()[0], device.p3()[1]+60, device.p3()[2]); alarmTip.s('all.blend', 'red');//Change this property to change the color of the model, because the model is already data bound when it is created return alarmTip; }
Next, let's see how to make this "alarm" node "flicker". I directly bind this animation to the node, so that the animation can be controlled directly through the node. So when we create the model of the alarm above, we can directly bind the animation to the node:
if(formPane){ alarmNode.scaleFunc = function() {//Set size change animation var size = alarmNode.s3();//Get the size of the node if (size[0] === 2 && size[1] === 2 && size[2] === 2) alarmNode.s3([1, 1, 1]); else alarmNode.s3 ([2, 2, 2]); alarmNode.scaleTimer = setTimeout(alarmNode.scaleFunc, formPane.v('scaleInterval'));//Set animation } alarmNode.blinkFunc = function(){//Set the blinking animation var color = alarmNode.s('all.blend');//Get the style of the node if (color === 'red') alarmNode.s({'all.blend': 'yellow'});//If the node color is red, set it to yellow else alarmNode.s({'all.blend': 'red'}); alarmNode.blinkTimer = setTimeout (alarmNode.blinkFunc, formPane.v ('blinkInterval')); } alarmNode.rotateFunc = function() {//Set the rotation animation alarmNode.setRotation(alarmNode.getRotation() + Math.PI/20);//Get the current rotation angle of the node, add Math.PI/20 angles on top of this rotation angle alarmNode.rotateTimer = setTimeout (alarmNode.rotateFunc, formPane.v ('rotInterval')); } }
In the animation above, I set the speed of the node flickering, the animation of the flickering node, etc., which can be controlled by the properties on the form form panel, mainly to talk about the implementation of this function on the form form:
formPane.addRow([//Add a row of elements to the form panel { checkBox: {//Check box label: 'Enable Blink',//The text content corresponding to the checkbox selected: true,//Set the selected checkbox onValueChanged: function(){//The function of the callback when the checkbox value changes var data = dataModel.getDataByTag('colorAlarm');//Get the node through the tag tag if (this.getValue()) {//Get the current value of the checkbox true/false data.blinkTimer = setTimeout(data.blinkFunc, formPane.v('blinkInterval'));//Set the animation directly by setting the blinkTimer of the node } else { clearTimeout(data.blinkTimer);//Clear animation } } } }, { id: 'blinkInterval',//form can get the value of this item through getValue (abbreviated as v) slider: {//After setting thisHT
propertyht.widget.Slider
, the object will be automatically constructed according to the property value and saved on theelement
property min: 0,//slider minimum value max: 1000, //the maximum value of the slider step: 50,//slider step value: 500,//The value of the current slider } } ], [0.1, 0.1]);//Set the value of the width of the two item elements in this line is less than 1 to the ratio
Finally, let's talk about the part where the balls flow on the 3D pipeline. This function is really very practical, and the effect is really good. I want to share it with you~
First, create a line to connect the start node and end node and set the style of the line. Use ht.Edge to attach the line to the start node and end node, so that any one of the two nodes can be moved. The connection will change with the position of the node movement, which is very convenient:
var polyline = new ht.Edge(source, target);//Create a connection dataModel.add(polyline);//Add the connection to the data container polyline.s({ 'edge.width': 5,//connection width 'edge.type': 'points',//When the connection type is points, the direction of the connection will be determined by the edge.points property, which is used to draw polylines 'edge.points': [//The array of point objects in {x:100, y:100} format of type ht.List can be set, which works when edge.type is points {x: source.getPosition3d()[0]+200, y: source.getPosition3d()[2], e: source.getPosition3d()[1]}, {x: target.getPosition3d()[0]+400, y: target.getPosition3d()[2], e: target.getPosition3d()[1]} ], 'edge.segments': [1, 4], // used to describe the point connection style, the array element is an integer value 'shape3d': 'cylinder',//cylinder 'shape3d.color': 'rgba(242, 200, 40, 0.4)', 'shape3d.resolution': 30,//Number of micro-segments, which can determine the smoothness of the curve 'edge.source.t3': [20, 0, 0],//connection source end offset, [tx, ty, tz] format, default is empty 'edge.target.t3': [20, 0, 0]//connection target end offset, [tx, ty, tz] format, default is empty });
Because the points we set when creating the connection are only two points on the curve, so if we want to obtain the points currently formed by the curve, the two points of source and target are missing. We reset an array and put these two points. Points are added, which will be used later to obtain all points on the curve:
var list = new ht.List(); list.push({x: source.getPosition3d()[0], y: source.getPosition3d()[2], e: source.getPosition3d()[1]});//Add source points to the array polyline.s('edge.points').each(function(item){//Add the two points that have been set in the style attribute list.push(item); }); list.push({x: target.getPosition3d()[0], y: target.getPosition3d()[2], e: target.getPosition3d()[1]});//添加target点
Then create a ball node that slides on the pipeline. This is only a setting node. When it is actually added to the data container dataModel, it needs to be added when the coordinates of the ball need to be set. If the position is not set for the node, add the node to the data container. , the initial position of the node is the position of the center [0, 0, 0] of the 3D scene. The animation code of the ball sliding is as follows:
var ball = new ht.Node();//Create a ball node ball.s({//Set the style of the ball node 'shape3d': 'sphere',//Set the 3d model of the ball to spherical 'shape3d.color': 'rgba(40, 90, 240, 0.4)'//Set the color of the 3d model }); var delta = 10, flag = 0; setInterval(function(){ flag++; var length = (polyline.a('total') || 0) % polyline.a('length') + delta;//The length of the curve that the ball currently travels var cache = ht.Default.getLineCacheInfo(list, polyline.s('edge.segments'));//Get the information of the points on the curve var lineLength = ht.Default.getLineLength(cache);//Get the total length of the curve polyline.a('length', lineLength - 50);//Because I set the edge's t3 (equivalent to the offset in 2d), the line length is actually not that long var offset = ht.Default.getLineOffset(cache, length);//The offset of the curve according to the information of the points on the curve ball.setPosition3d(offset.point.x + 10, offset.point.y, offset.point.z);//Set the coordinates of the node polyline.a('total', length); if(flag === 1) dataModel.add(ball);//The node already has coordinates and can be added to the data container }, 10);
We can also see that there are two special polygons "parallelogram" and "trapezoid" on the second layer. The parallelogram is based on the createParallelogramModel model function, which is relatively simple, createExtrusionModel(array, segments, top, bottom, resolution, repeatUVLength , tall, elevation), array is the coordinate point of the graph you want to form, this is just for the plane graph drawn on the xz axis, segments refers to how to connect these coordinate points, please refer to the HT for Web shape manual ( http://hightopo.com/guide/guide/core/shape/ht-shape-guide.html ), top and bottom are to let you choose whether there is a top or bottom, the resolution is the number of micro-segments, when we draw a curve, we may only need Confirm a few individual points and then divide it into multiple segments on the line between each two points, so that this line segment will become smooth, ht encapsulates this parameter so that users can easily manipulate these line segments , repeatUVLength is empty by default. After setting the value, the top and bottom textures will be repeated according to the specified length value. The height of the tall model is 5 by default. The y-axis position of the elevation model center is 0 by default. Setting this value can make xz The plane on is rotated around the y-axis.
The effect of a ring at the bottom is achieved through an algorithm. The ring has to confirm how many elements there are on the ring, and then calculate the angle between each two, and calculate the position of each element through sin and cos, and get the following code:
names = ['Device 2', 'Device 3', 'Device 4', 'Device 5', 'Device 6', 'Device 7', 'Device 8', 'Device 9']; names.forEach(function(name, index) { x = 400, y = 200, angle = 45, r = 120; x = x3 + Math.sin((2 * Math.PI / 360) * angle * index) * r; y = z3 + Math.cos((2 * Math.PI / 360) * angle * index) * r; device = createRect([x, y3 + 15, y], [w * 0.1, 15, h * 0.1], '', '', floor3); createEdge(device5, device); });
If there are other parts that you don't understand, you can go to the official website ( http://hightopo.com ) to check the corresponding manual, or leave a private message.
An example of this article is attached: http://www.hightopo.com/demo/3DTopology/index.html