3D computer rooms rendered with WebGL are nothing new now. The main purpose of this article is to explain the problem of eye and center in 3D computer rooms, which happened to be used in the project. After thinking about it for a while, finally I feel that this example best meets my requirements, so I use it as a record.
renderingsThe demo of this 3D computer room is pretty good, it’s pretty, and the basic interactions are satisfactory. Let’s see how to implement it.
code generation Define classFirst, open the corresponding js one by one from the js path called in index.html. An Editor.Server class is customized in server.js and is created by the ht.Default.def function encapsulated by HT (note that the created class name is Editor The Editor in front of .Server cannot be replaced with E):
ht.Default.def('Editor.Server', Object, {//The first parameter is the class name. If it is a string, it will be automatically registered in HT's classMap; the second parameter is the parent class to be inherited by this class. ; The third parameter is the declaration of methods and variables addToDataModel: function(dm) { //Add the node to the data container dm.add(this._node); // The predefined function in ht, pass the node through add Method added to the data container}, setHost: function() { //Set the adsorption this._node.setHost.apply(this._node, arguments); }, s3: function() {//Set the size of the node this._node .s3.apply(this._node, arguments); }, setElevation: function() {//Control the y-axis position of the 3D coordinate system where the center position of the Node primitive is located this._node.setElevation.apply(this._node, arguments); }});
Create Editor.Server class
This class can create an ht.Node node and set the node's color and front texture:
var S = E.Server = function(obj) {//Server component var color = obj.color, frontImg = obj.frontImg; var node = this._node = new ht.Node();//Create node node.s ({//Set the style s of the node to the abbreviation of setStyle 'all.color': color,//Set the color of the six sides of the node 'front.image': frontImg //Set the image on the front of the node});};
In this way, I can directly create a new server component object where I need to create the server component, and I can directly call the setHost and other functions we declared above, which we will use soon.
Next, create the Editor.Cabinet cabinet class. The method is similar to the definition method of the Editor.Server class above:
ht.Default.def('Editor.Cabinet', Object, { addToDataModel: function(dm) { dm.add(this._door); dm.add(this._node); this._serverList.forEach(function(s) { s.addToDataModel(dm); }); }, p3: function() { this._node.p3.apply(this._node, arguments);//Set the 3d coordinates of the node}});
Create Editor.Cabinet class
This class is relatively more complex than the previous Editor.Server server component class. This class creates a cabinet body, cabinet door, and server components inside the cabinet:
var C = E.Cabinet = function(obj) { var color = obj.color, doorFrontImg = obj.doorFrontImg, doorBackImg = obj.doorBackImg, s3 = obj.s3; var node = this._node = new ht.Node() ; // Cabinet node.s3(s3); //Set the size of the node to setSize3d node.a('cabinet', this);//Customize the cabinet properties node.s({//Set the node style to setStyle 'all.color': color,//Set the color of the six sides of the node 'front.visible ': false//Set whether the front of the node is visible}); if (Math.random() > 0.5) { node.addStyleIcon('alarm', {//Add icon names to the node: ['icon thermometer'], //An array containing multiple strings, each string corresponds to a picture or vector (registered through ht.Default.setImage) face: 'top', //The default value is front, icon Orientation in 3D, available values are left|right|top|bottom|front|back|center position: 17, //Specify the position of icons autorotate: 'y', //The default value is false, whether the icon automatically faces the direction of the eye in 3D t3: [0, 16, 0], //The default value is undefined, the offset of the icon in 3D, the format is [x ,y,z] width: 37,//Specify the width of each icon, the default is based on the width when registering the image height: 32,//Specify the height of each icon, the default is based on the height when registering the image textureScale: 4, //The default value is 2. This value represents the multiple of the actual map generated by the memory. It should not be set too large otherwise it will affect the performance. visible: { func: function() { return !!E.alarmVisible; }}//Indicates the group of pictures. Whether to display }); } var door = this._door = new ht.DoorWindow();//Cabinet door door.setWidth(s3[0]);//Set the length of the graphic element in the x-axis direction in the 3D topology door.setHeight(1);//Set the graphic element in the 3D topology z-axis in Length door.setTall(s3[1]);//Control the length of the Node primitive on the y-axis door.setElevation(0);//Set the y coordinate of the center of the primitive in the 3D coordinate system door.setY(s3[2 ] * 0.5);//Set the position of the node on the y-axis door.setHost(node);//Set the adsorption door.s({//Set the node style setStyle 'all.color': color,//Set the color of the six sides of the node 'front.image': doorFrontImg, //Set the front image of the node 'front.transparent': true, //Set whether the front face of the node is transparent 'back.image': doorBackImg, //Set the image on the back of the node 'back.uv': [1,0, 1,1, 0,1, 0,0],//Customize the uv map behind the node. If it is empty, the default value [0,0, 0,1, 1,1, 1,0] ' dw.axis': 'right'//Set the rotation axis for DoorWindow element expansion and closing operations, the possible values are left|right|top|bottom|v|h }); var serverList = this._serverList = []; var max = 6, list = E.randomList(max, Math.floor(Math.random() * (max - 2)) + 2); //Function to obtain random numbers declared in global.js var server, h = s3[0] / 4; list.forEach(function(r) { var server = new E.Server({ //Server component color: 'rgb(51,49,49)', frontImg: 'Server component fine' }); server.s3(s3[0] - 2, h, s3[2] - 4);//Set the node size server.setElevation((r - max * 0.5) * (h + 2 ));//Set the coordinates of the node center point on the y-axis server.setHost(node);//Set the adsorption of the node serverList.push(server);//To serverList Add server node in });};
The only thing not mentioned in the above code is the Editor.randomList function. This function is declared in the global.js file and is declared as follows:
var E = window.Editor = { leftWidth: 0, topHeight: 40, randomList: function(max, size) { var list = [], ran; while (list.length < size) { ran = Math.floor(Math. random() * max); if (list.indexOf(ran) >= 0) continue; list.push(ran); } return list; }};
Okay, now that the classes for each part of the scene have been created, we should create the scene and then pile these primitives into it!
Scene creationStudents who are familiar with it should know that using HT to create a 3D scene only requires a new 3D component, and then add the scene to the body through the addToDOM function:
var g3d = E.main = new ht.graph3d.Graph3dView(); //3d scene
The main.js file mainly does some necessary elements in the 3D scene, such as walls, floors, doors, air conditioners, and the generation and discharge positions of all cabinets, as well as very important interactive parts.
I will not post the code for the creation of walls, floors, doors, air conditioners and cabinets. If you are interested, please check the code yourself. Here we mainly talk about double-clicking the cabinet and any objects related to the cabinet (cabinet doors, server equipment) to create 3D The center of the camera's line of sight will move to a certain position in front of the double-clicked cabinet, and this movement is very smooth. I was not good at it before, so I thought about this part for a long time, and finally referred to the implementation method of this Demo.
In order to be able to repeatedly set eye and center, the content corresponding to setting these two parameters is encapsulated into the setEye and setCenter methods. The setCenter method is similar to the setEye method and will not be repeated here:
//Set the eye position var setEye = function(eye, finish) { if (!eye) return; var e = g3d.getEye().slice(0),//Get the current eye value dx = eye[0] - e[0], dy = eye[1] - e[1], dz = eye[2] - e[2]; // Start a 500 millisecond animation transition ht.Default.startAnim({ duration: 500, easing: easing,//Animation easing function finishFunc: finish || function() {}, //Function called after the animation ends action: function(v, t) {//Set animation v to represent passing easing (t) The value after function operation, t represents the progress of the current animation [0~1], general attribute changes are based on the v parameter g3d.setEye([ //Set the eye in the 3D scene The value of the eye is an array, corresponding to the values of the x, y, and z axes respectively e[0] + dx * v, e[1] + dy * v, e[2] + dz * v ]); } }) ;};
The fact that I did not repeatedly declare the setCenter function does not mean that this function is not important. On the contrary, this function plays a decisive role in the process of moving the line of sight. The above setEye function is equivalent to me wanting to walk in front of my target position (at least I define is used for this purpose), while sCenter The definition is to move my sight to the position of the target (for example, I can stand at my current position and look at the object behind me to the right, or I can go to the back to my right and stand in front of the object to look at it). It is very important, please savor it.
The double-click event is simple. Just listen to the event encapsulated by HT, determine the event type, and take corresponding actions:
g3d.mi(function(e) {//addInteractorListener event listening function if (e.kind !== 'doubleClickData') //Determine the event type to be a double-click node return; var data = e.data, p3; if (data. a('cabinet')) //Body p3 = data.p3(); else { host = data.getHost(); //Get the adsorption object of the clicked node if (host && host.a('cabinet')) {//If the adsorption object is cabinet p3 = host.p3(); } } if (!p3) return; setCenter(p3); //Set the center target's moving position to The position of the cabinet setEye([p3[0], 211, p3[2] + 247]); //Set the position where the eye will move});top navigation bar
When I first saw this example, I was thinking, this person is so awesome. I have been using HT for so long, but I still haven’t been able to create such beautiful effects using HT’s ht.widget.Toolbar. When I looked at it, I realized that this was actually using HT. It's made with form, it's amazing, I'm so stupid.
var form = E.top = new ht.widget.FormPane(); //Top form component form.setRowHeight(E.topHeight);//Set the row height form.setVGap(-E.topHeight);//Set the horizontal spacing of the form component to a negative value of the row height to keep multiple rows in the same position Line form.setVPadding(0);//Set the top of the form and the spacing between the top and the component content form.addRow([null, {//Add a row of components to the form. The first parameter is an array of elements. The elements can be strings, component parameter information described in json format, html elements, or null image: { icon: './symbols/inputBG.json ', stretch: 'centerUniform' }}], [40, 260]);//The second parameter is an array of width information for each element. A width value greater than 1 represents a fixed absolute value, and a width value less than or equal to 1 represents a relative value. It can also be a combination of 80+0.3 form.addRow([null, null , { id: 'searchInput', textField: {}}, { element: 'Computer room visual management system', color: 'white', font: '18px arial, sans-serif'}, null, { button: { // label: 'ViewChange', icon: './symbols/viewChange.json', background: null, selectBackground: 'rgb(128,128,128)', borderColor: 'rgba(0, 0, 0, 0 )', onClicked: function() { E.focusTo(); } }}, null, { button: { // label: 'Alert', icon: './symbols/alarm.json', togglable: true, selected: false, background: null, selectBackground: 'rgb(128,128,128)', borderColor: 'rgba(0, 0, 0, 0)', onClicked: function( e) { E.setAlarmVisible(this.isSelected()); } }}, null], [40, 42, 218, 300, 0.1, 50, 10, 50, 10]);
The above is only possible, but it is not actually added to the html tag, which means that there is nothing on the interface now! Don't forget to add the 3D scene to the body when the page loads, and don't forget to add the form to the body. When setting the window size change event, the form also needs to be updated in real time:
window.addEventListener('load', function() { g3d.addToDOM(); //Add the 3D scene into the body document.body.appendChild(E.top.getView()); //Add the underlying div of the form component Add to the body window.addEventListener('resize', function() {//Listen for window size change events E.top.iv();//Update the underlying div of the form form });});
Here is an explanation of the addToDOM function, which is very important for understanding the mechanism of HT. HT components are generally embedded in containers such as BorderPane, SplitView and TabView. The outermost HT component requires the user to manually add the underlying div element returned by getView() to the DOM element of the page. What needs to be noted here is that, When the size of the parent container changes, if the parent container is HT's predefined container components such as BorderPane and SplitView, the HT container will automatically call the invalidate function of the child component recursively to notify the update. But if the parent container is a native html element, the HT component cannot know that it needs to be updated. Therefore, the outermost HT component generally needs to listen to the window size change event of the window and call the invalidate function of the outermost component to update.
In order to facilitate the loading of the outermost component to fill the window, all components of HT have the addToDOM function, and its implementation logic is as follows, where iv is the abbreviation of invalidate:
addToDOM = function(){ var self = this, view = self.getView(), style = view.style; document.body.appendChild(view); //Add the underlying div of the scene into the body style.left = ' 0';//HT sets the position of the underlying div of all components to absolute style.right = '0'; style.top = '0'; style.bottom = '0'; window.addEventListener('resize', function () { self.iv(); }, false); //Listen events for window size changes and notify component changes and updates}
In this way, all the code is over. You can right-click to check it yourself, and the corresponding json file can be obtained from the network.
The above is the entire content of this article. I hope it will be helpful to everyone’s study. I also hope everyone will support VeVb Wulin Network.