const socket = io({path:base_path + "/socket.io"}); const myId = Date.now(); let camera, scene, renderer, clock, controls; let dirLight, pointLight; var ships = []; var fishes = []; var birds = []; var moving_objects = []; var rotor, light_house, palm, man_pers, whale, seagull; let geometry; let material; var island_group = new THREE.Group(); var border_cube_group = new THREE.Group(); var delta = 0; var colors = ["orange", "blue", "maroon", "olive", "silver", "purple", "lime", "blue", "red", "pink"]; // 60 fps var interval = 1 / 60; const raycaster = new THREE.Raycaster(); const pointer = new THREE.Vector2(); var partical_material, pointsShader; var partical_points = []; var collision_distance = 4; var mouse_down = false; var border_cube = []; var active_border_cube, last_selected_border_cube; var test = false; var tt = 1; var manual_drive = false; var active_border; var border_points = []; var prom = []; var ship_init = { x: -5, y: 7, angle: 40 } var sea_not_clicked = false; init(); function init() { initScene(); initMisc(); window.addEventListener('keydown', function(event) { const key = event.key; // "ArrowRight", "ArrowLeft", "ArrowUp", or "ArrowDown" // var object = ships[myId]; var object = seagull; if(key == "ArrowUp"){ if(!object.before_collision_bezier){ var t_step = 0.002; } else{ var t_step = 0.005; } object.path += t_step; } if(key == "ArrowDown"){ if(!object.before_collision_bezier){ var t_step = 0.002; } else{ var t_step = 0.005; } object.path -= t_step; } }); window.addEventListener( 'resize', onWindowResize ); canvas.addEventListener( 'mousedown', function(){mouse_down = true} ); canvas.addEventListener( 'mousemove', function(){if(!ships[myId].moving_stopped || !active_border_cube) mouse_down = false} ); canvas.addEventListener( 'mouseup', function(e){if(mouse_down) onPointerClick(e)} ); var range = document.querySelector("#range"); range.addEventListener("input", function(){ floor.scale.x = 100 / ( 101 - this.value); floor.scale.y = 100 / ( 101 - this.value); }); // window.addEventListener("contextmenu", function(){ // active_border.splice(-1, 1); // border_cube_group.remove( border_cube.splice(-1, 1)[0] ); // }); window.addEventListener( 'pointermove', onPointerMove ); map.checked = false; collision_edit.checked = false; map.addEventListener( 'click', function(){ if(canvas2.style.display == "none" || canvas2.style.display == ""){ canvas2.style.display = "block"; canvas3.style.display = "block"; drawMapAndBorders(1); } else{ canvas2.style.display = "none"; canvas3.style.display = "none"; } } ); collision_edit.addEventListener( 'click', function(){ ships[myId].moving_stopped = true; test = this.checked; drawMapAndBorders(1); for(var i = 0; i < border_cube_group.children.length; i++){ border_cube_group.children[i].visible = this.checked; } if(!test){ sheet1.classList.remove("hide"); sheet2.classList.add("hide"); lagr_test.disabled = true; ships[myId].collision = false; ships[myId].position.z = ship_init.y; ships[myId].position.x = ship_init.x; ships[myId].rotation.y = ship_init.angle / 180 * Math.PI; ships[myId].path = 1.1; // при t больше единицы движение по кривой Безье остановится ships[myId].path2 = 0; } else{ sheet1.classList.add("hide"); sheet2.classList.remove("hide"); lagr_test.disabled = false; shipLagrInit(); } } ); } function shipLagrInit(){ ships[myId].before_collision = false; ships[myId].before_collision_bezier = false; ships[myId].collision = true; ships[myId].border_line_num = 0; ships[myId].position.x = active_border[0].x; ships[myId].position.z = active_border[0].y; ships[myId].path2 = active_border[0].x; ships[myId].lagr_direction = 1; } lagr_test.addEventListener( 'click', function(){ shipLagrInit(); } ); border_left.addEventListener( 'click', function(){ active_border = border_points[1]; } ); border_right.addEventListener( 'click', function(){ active_border = border_points[0]; } ); function drawLagr(){ ctx3.clearRect(0, 0, canvas2.width, canvas2.height); var angle = getAngle({}, active_border[0].x, active_border[0].y, active_border[0 + 1].x, active_border[0 + 1].y, true).angle; var step = 1 * Math.cos(angle); var n = 0; for(var x = active_border[0].x; x < active_border[active_border.length - 1].x; x+=step){ for(var i = n + 1; i < active_border.length - 1; i++){ if(x > active_border[i].x){ angle = getAngle({}, active_border[i].x, active_border[i].y, active_border[i + 1].x, active_border[i + 1].y, true).angle; step = 1 * Math.cos(angle); n++; } } var y = lagr(x, active_border); ctx3.fillStyle = "red"; ctx3.fillRect(y*10 + 375, -x*10 + 250, 3,3); } } function initScene() { camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 1000 ); camera.position.set( 0, 15, 35 ); scene = new THREE.Scene(); pointLight = new THREE.PointLight( 0xDDDDDD, 1, 100 ); pointLight.name = 'Spot Light'; pointLight.intensity = 10; pointLight.decay = 0.2; pointLight.position.set(0, 10, 0); pointLight.castShadow = true; pointLight.receiveShadow = true; scene.add( pointLight ); } function initMisc() { clock = new THREE.Clock(); window.canvas = document.querySelector('#c'); renderer = new THREE.WebGLRenderer({canvas, antialias: true}); renderer.setPixelRatio( window.devicePixelRatio ); renderer.setSize( window.innerWidth, window.innerHeight ); updateAll(); // Mouse control controls = new THREE.OrbitControls( camera, canvas ); controls.target.set( 0, 2, 0 ); controls.update(); } function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize( window.innerWidth, window.innerHeight ); } function collision(obj, test){ if(obj && obj.position){ var angle = obj.rotation.y; var params = getRadiusCoords(obj.position.x, obj.position.z, 25, angle); // Точка на расстоянии 0.5 от центра объекта — нос корабля, к примеру. var obj2 = {x: params.x, y: params.y}; for(var k = 0; k <= 1; k++){ for(var i = 0; i < border_points[k].length - 1; i++){ if(canvas2.style.display != "none"){ drawMapAndBorders(i); } var obj3 = border_points[k][i]; var obj4 = border_points[k][i + 1]; var r = Math.sqrt(Math.pow( obj.position.x - obj3.x , 2) + Math.pow(obj.position.z - obj3.y , 2)); if( r <= collision_distance && !obj.lagrange_stopped || test){ if(!obj.before_collision && !obj.before_collision_bezier && !obj.collision){ var intersection = lineIntersection({x: obj.position.x, y: obj.position.z}, {x: obj2.x, y: obj2.y}, obj3, obj4); var x = intersection.x; var y = intersection.y; if(obj4.y >= obj3.y){ var condition = x >= obj3.x && x <= obj4.x && y >= obj3.y && y <= obj4.y; } else{ var condition = x >= obj3.x && x <= obj4.x && y >= obj4.y && y <= obj3.y; } if(condition){ obj.border_line_angle = getAngle({}, obj3.x, obj3.y, obj4.x, obj4.y, true).angle; obj.border_line_num = i; ctx3.clearRect(0, 0, canvas2.width, canvas2.height); drawCross(x, y, "white", size = 0.5); active_border = border_points[k]; if(!test){ if(i < border_points[k].length / 2 - 1){ obj.lagr_direction = -1; } else{ obj.lagr_direction = 1; } obj.path2 = x; // obj.lagr_direction = -1; // obj.path2 = border_points[k][0].x; } else{ obj.path2 = 0; } return r; } // else{ // return false; // } } } } } // return false; } // else{ // return false; // } return false; } function renderScene() { // var object = scene.getObjectByName("Scene"); if(typeof man_pers != "undefined"){ if(typeof man_pers.mixer != "undefined" && light_house && light_house.children){ var light_house_plate = light_house.children[0].children[0].children[0].children[0].children[2].geometry.boundingSphere; let x0 = light_house_plate.center.x * light_house.scale.x; let y0 = -light_house_plate.center.y * light_house.scale.y; var params = getRadiusCoords(x0, y0, light_house_plate.radius * light_house.scale.x - 1.3, man_pers.angle_for_rotation * Math.PI / 180); x0 = man_pers.position.x; y0 = man_pers.position.z; man_pers.position.x = params.x; man_pers.position.z = params.y; getAngle( man_pers, x0, y0, man_pers.position.x, man_pers.position.z); man_pers.rotation.y = man_pers.angle + Math.PI/2; man_pers.position.y = 1.9; } } if(typeof man_pers != "undefined" && man_pers){ man_pers.angle_for_rotation += man_pers.step; if(man_pers.angle_for_rotation > 360){ man_pers.angle_for_rotation = 0; } } floor.material.uniforms[ 'time' ].value += 0.1 / 60.0; if(pointsShader){ pointsShader.uniforms[ 'time' ].value += 0.1 / 60.0; } if(window.palmShader && window.palmShader.length >= 1){ for(var i in palm){ if(window.palmShader[i]){ palm[i].angle_for_rotation++; window.palmShader[i].uniforms[ 'uTime' ].value = palm[i].angle_for_rotation * Math.PI / 180; } } } for(var k in moving_objects){ var object = moving_objects[k]; if(object && !object.test_stop){ object.test_stop = 0; } // if(object && object.test_stop < tt || manual_drive){ if(object){ if(!object.rotation_step){ object.angle_for_rotation++; } else{ object.angle_for_rotation += object.rotation_step; } if(object.angle_for_rotation > 360){ object.angle_for_rotation = 0; } object.move(object); } } if(typeof man_pers != "undefined"){ glow_sphere.scale.x = Math.cos(man_pers.angle_for_rotation * Math.PI / 180); glow_sphere.scale.y = Math.cos(man_pers.angle_for_rotation * Math.PI / 180); glow_sphere.scale.z = Math.cos(man_pers.angle_for_rotation * Math.PI / 180); glow_sphere.material.uniforms.viewVector.value = new THREE.Vector3().subVectors( camera.position, new THREE.Vector3(island_group.position.x + glow_sphere.position.x, island_group.position.y + glow_sphere.position.y, island_group.position.z + glow_sphere.position.z)); } updateParticles(); if(border_cube_group.children.length > 0 && test){ intersectBorderCube(); } renderer.render( scene, camera ); } function render() { renderScene(); } function updateAll() { requestAnimationFrame(updateAll); delta += clock.getDelta(); if (delta > interval) { // The draw or time dependent code are here render(); delta = delta % interval; } } ////////////////////////////// Move functions //////////////////////////////////// function fishMove(object, minz = -10, maxz = 0.2){ if(object.path == undefined || object.path >= 0.9){ // alert(); object.collision = false; object.path = 0; var x0 = object.position.x0 = object.position.x; var y0 = object.position.y0 = object.position.z; var z0 = object.position.z0 = object.position.y; object.step = getRandom(2, 5) / 1000; if(object == whale){ var random_radius = getRandom(5, 20); var random_angle = getRandom(0, Math.PI * 2); var random_target = getRadiusCoords(0, 0, random_radius, random_angle); object.position.x_end = random_target.x; object.position.y_end = random_target.y; object.position.z_end = getRandom(maxz, minz); } else{ object.position.x_end = getRandom(-20, 20); object.position.y_end = getRandom(-20, 20); object.position.z_end = getRandom(maxz, minz); } var params; params = getAngle(object, x0, y0, object.position.x_end, object.position.y_end); object.angle_old = object.rotation.y - Math.PI / 2; var r = object.hipotenuse; params = getRadiusCoords(x0, y0, object.hipotenuse / 10, object.angle_old); params2 = getRadiusCoords(x0, y0, r, object.angle_old + 10 / 180 * Math.PI); object.p = []; if(params.x){ object.p[0] = { x: object.position.x0, y: object.position.y0, z: object.position.z0, } object.p[1] = { x: params.x, y: params.y, z: getRandom(maxz, minz) } object.p[2] = { x: params2.x, y: params2.y, z: getRandom(maxz, minz) } object.p[3] = { x: object.position.x_end, y: object.position.y_end, z: object.position.z_end, } } } if(object.p || test){ if(object == whale){ object.rotation.z = object.angle_for_rotation / 180 * Math.PI; } if( (!object.collision || object.lagrange_stopped) && !test ){ if(object.path == undefined){ object.path = 0; } if(object.path <= 1){ if(!object.before_collision_bezier){ var t_step = object.step; } else{ var t_step = 0.005; } if(!manual_drive){ object.path += t_step - t_step * object.path; } } else{ object.moving_stopped = true; } } if(!test){ var t = object.path; var p0 = object.p[0]; var p1 = object.p[1]; var p2 = object.p[2]; } /////////////////// var x0 = object.position.x; var y0 = object.position.z; var z0 = object.position.y; /////////////////// if(!object.moving_stopped && t <= 1){ p0 = object.p[0]; p1 = object.p[1]; p2 = object.p[2]; var p3 = object.p[3]; var p4 = object.p[4]; object.position.x = Math.pow(1 - t, 3) * p0.x + 3 * t * Math.pow(1 - t, 2) * p1.x + 3 * Math.pow(t, 2) * (1 - t) * p2.x + Math.pow(t, 3) * p3.x; object.position.z = Math.pow(1 - t, 3) * p0.y + 3 * t * Math.pow(1 - t, 2) * p1.y + 3 * Math.pow(t, 2) * (1 - t) * p2.y + Math.pow(t, 3) * p3.y; object.position.y = Math.pow(1 - t, 3) * p0.z + 3 * t * Math.pow(1 - t, 2) * p1.z + 3 * Math.pow(t, 2) * (1 - t) * p2.z + Math.pow(t, 3) * p3.z; } // ctx3.fillRect(whale.position.z*10 + 375, -whale.position.x*10 + 250, 1, 1); if(t <= 1){ getAngle(object, x0, y0, object.position.x, object.position.z); if(object.angle){ object.rotation.y = object.angle + Math.PI / 2; } } if(t >= 1 && object.before_collision_bezier){ // object.before_collision_bezier = false; // object.collision = true; } } } function shipMove(object){ object.position.y = -0.3 + Math.cos(object.angle_for_rotation / 180 * Math.PI + Math.PI / 2) / 10; object.rotation.x = Math.cos(object.angle_for_rotation / 180 * Math.PI) / 5; if(object.p || test){ var r = collision(object, test) ; // Детекция коллизии if( r && !object.before_collision && ! object.before_collision_bezier && !object.collision){ object.before_collision = true; object.angle_for_rotation = 0; object.path = 0; let x0 = object.position.x0 = object.position.x; let y0 = object.position.y0 = object.position.z; let params, params2, params3; params = getAngle(object, x0, y0, object.position.x_end, object.position.y_end); if(object.rotation.y){ object.angle_old = object.rotation.y; } } if( (!object.collision || object.lagrange_stopped) && !test ){ if(object.path == undefined){ object.path = 0; } if(object.path <= 1){ if(!object.before_collision_bezier){ var t_step = 0.002; } else{ var t_step = 0.005; } if(!manual_drive){ object.path += t_step; } rotor.rotation.x = object.angle_for_rotation / 180 * Math.PI * 10; } else{ object.moving_stopped = true; } } if(!object.moving_stopped){ rotor.rotation.x = object.angle_for_rotation / 180 * Math.PI * 10; } if(!test){ var t = object.path; var p0 = object.p[0]; var p1 = object.p[1]; var p2 = object.p[2]; } /////////////////// let x0 = object.position.x; let y0 = object.position.z; /////////////////// if(object.before_collision){ object.path = 0; object.position.x0 = object.position.x; object.position.y0 = object.position.z; var params, params2; params = getAngle(object, x0, y0, object.position.x_end, object.position.y_end); object.angle_old = object.rotation.y; var r = 5; var x_bezier_end = object.path2; var y_bezier_end = lagr(object.path2, active_border); // let angle = getAngle({}, x0, y0, x_bezier_end, y_bezier_end, true).angle; params = getRadiusCoords(x0, y0, object.hipotenuse / 10, object.angle_old); var line_num = object.border_line_num; if(object.lagr_direction > 0){ var determ = Math.PI; } else{ var determ = Math.PI * 2; } params2 = getRadiusCoords(active_border[line_num].x, active_border[line_num].y, r, object.border_line_angle + determ); object.p[0] = { x: object.position.x, y: object.position.z } object.p[1] = { x: params.x, y: params.y } object.p[2] = { x: params2.x, y: params2.y } object.p[3] = { x: x_bezier_end, y: y_bezier_end } object.before_collision = false; object.before_collision_bezier = true; } if(object.collision){ if(!test){ var p = object.p; } else{ var p = []; object.p = []; } if(object.path2 != undefined){ object.path2 += 0.02 * object.lagr_direction; } // Окончание продвижения по кривой Лагранжа окончено if(object.lagr_direction == 1){ var condition = object.position.x >= active_border[active_border.length - 1].x; } else{ var condition = object.position.x <= active_border[0].x; } if(condition){ object.collision = false; object.lagrange_stopped = true; object.angle_for_rotation = 0; object.path = 0; object.position.x0 = object.position.x; object.position.y0 = object.position.z; var params; params = getAngle(object, x0, y0, object.position.x_end, object.position.y_end, true); object.angle_old = object.rotation.y; var r = object.hipotenuse; params = getRadiusCoords(x0, y0, object.hipotenuse / 10, object.angle_old); params2 = getRadiusCoords(x0, y0, r, object.angle_old + 10 / 180 * Math.PI); object.p[0] = { x: object.position.x, y: object.position.z } object.p[1] = { x: params.x, y: params.y } object.p[2] = { x: params2.x, y: params2.y } object.p[3] = { x: object.position.x_end, y: object.position.y_end } } // Движение по кривой Лагранжа var x = object.path2; var y = lagr(x, active_border); // Лагранж object.position.x = x; object.position.z = y; } else if(!object.moving_stopped && t <= 1){ p0 = object.p[0]; p1 = object.p[1]; p2 = object.p[2]; var p3 = object.p[3]; var p4 = object.p[4]; object.position.x = Math.pow(1 - t, 3) * p0.x + 3 * t * Math.pow(1 - t, 2) * p1.x + 3 * Math.pow(t, 2) * (1 - t) * p2.x + Math.pow(t, 3) * p3.x; object.position.z = Math.pow(1 - t, 3) * p0.y + 3 * t * Math.pow(1 - t, 2) * p1.y + 3 * Math.pow(t, 2) * (1 - t) * p2.y + Math.pow(t, 3) * p3.y; } // В начале движения по безье бывает какой-то резкий скачок угла по сравнению с предыдущим положением, // поэтому первый шаг c приращением t прпускаем if(t <= 1 && t > t_step || object.collision){ getAngle(object, x0, y0, object.position.x, object.position.z); if(object.angle){ object.rotation.y = object.angle; if(object.before_collision_bezier){ // console.log(t); ; object.test_stop++; } } } if(t >= 1 && object.before_collision_bezier){ object.before_collision_bezier = false; object.collision = true; } } } ////////////////////////////// Mathematical functions //////////////////////////////////// function getRadiusCoords(x0, y0, radius, angle){ var x = x0 + radius * Math.cos(angle); var y = y0 - radius * Math.sin(angle); return {x: x, y: y, angle: angle} } function getAngle(object, x0, y0, x_end, y_end, flag = false){ var a = y0 - y_end; var b = x0 - x_end; var hipotenuse_squared = Math.pow(a, 2) + Math.pow(b, 2); var hipotenuse = object.hipotenuse = Math.sqrt(hipotenuse_squared); var sina = object.sina = a / hipotenuse; var cosa = object.cosa = b / hipotenuse; if(!flag){ if(sina > 0){ object.angle = Math.PI - Math.acos(cosa); } else{ object.angle = Math.PI + Math.acos(cosa); } } else{ if(sina > 0){ var angle = Math.PI - Math.acos(cosa); } else{ var angle = Math.PI + Math.acos(cosa); } } return {sin: sina, cos: cosa, angle: angle} } function getRandom(min, max) { const minCeiled = Math.ceil(min); const maxFloored = Math.floor(max); return Math.floor(Math.random() * (maxFloored - minCeiled) + minCeiled); // The maximum is exclusive and the minimum is inclusive } function lagr(x0, points){ var y0=0;//значение многочлена в точке y0 var step; for(var i=0;i{ if(obj.id != myId){ var obj2 = ships[obj.id]; if(obj.x && ships[obj.id]){ ships[obj.id].rotation.y = obj.angle; onPointerClick(undefined, ships[obj.id], {x: obj.x, y: obj.y}); } } }) socket.on("connect", ()=> { socket.on("send_objects", (obj_collection)=>{; for(var i in obj_collection){ var obj = obj_collection[i]; var obj2 = ships[i]; if(!obj2){ obj2 = ships[myId].clone(); obj2.move = function(object){shipMove(object)}; obj2.position.z = obj.y0; obj2.position.x = obj.x0; obj2.rotation.y = obj.angle; obj2.angle_for_rotation = 0; obj2.rotation.z = 0; ships[obj.id] = obj2; moving_objects[obj.id] = obj2; scene.add(obj2); ships[obj.id].getObjectByName("Scene").getObjectByName("ship").material = ships[myId].getObjectByName("Scene").getObjectByName("ship").material.clone(); var color = colors[obj.id.toString().slice(-1)]; ships[obj.id].getObjectByName("Scene").getObjectByName("ship").material.color.set( color ); if(obj.x){ onPointerClick(undefined, ships[obj.id], {x: obj.x, y: obj.y}); } } } }) }) socket.on("remove", (id) => { scene.remove(ships[id]); delete ships[id]; delete moving_objects[id]; scene.remove(partical_points[id]); delete partical_points[id]; }) function onPointerClick(e, object, end_coords) { ctx3.clearRect(0, 0, canvas2.width, canvas2.height); controls.enableRotate = true; if(test){ intersectBorderCube(); } if(!object){ var canvasBoundingRect = canvas.getBoundingClientRect(); var t = canvasBoundingRect; pointer.x = (e.clientX - t.left) / t.width * 2 - 1; pointer.y = -(e.clientY - t.top) / t.height * 2 + 1; if(scene.getObjectByName("Scene")){ // scene.getObjectByName("Scene").position.x = pointer.y; // scene.getObjectByName("Scene").position.z = pointer.x; scene.getObjectByName("Scene").position.y = -0.3; } raycaster.setFromCamera( pointer, camera ); var intersects2 = raycaster.intersectObjects( light_house.children[0].children[0].children[0].children[0].children ); if(intersects2[0]){ sea_not_clicked = true; } var intersects = raycaster.intersectObjects( [floor] ); var object = ships[myId]; if(intersects[0] && !sea_not_clicked){ socket.emit("mouseclick", {x0: object.position.x, y0: object.position.z, x: intersects[0].point.x, y: intersects[0].point.z, angle: object.rotation.y, id: myId}); } } else{ var intersects = [{point: {x: end_coords.x, z: end_coords.y}}]; } if(object && intersects[0] && !sea_not_clicked){ object.collision = false; object.lagrange_stopped = false; object.angle_for_rotation = 0; object.path = 0; if(!collision_edit.checked){ object.moving_stopped = false; } var x0 = object.position.x0 = object.position.x; var y0 = object.position.y0 = object.position.z; object.position.x_end = intersects[0].point.x; object.position.y_end = intersects[0].point.z; var params; params = getAngle(object, x0, y0, object.position.x_end, object.position.y_end); object.angle_old = object.rotation.y; var r = object.hipotenuse; params = getRadiusCoords(x0, y0, object.hipotenuse / 10, object.angle_old); params2 = getRadiusCoords(x0, y0, r, object.angle_old + 10 / 180 * Math.PI); object.p = []; if(params.x){ object.p[0] = { x: object.position.x0, y: object.position.y0 } object.p[1] = { x: params.x, y: params.y } object.p[2] = { x: params2.x, y: params2.y } object.p[3] = { x: object.position.x_end, y: object.position.y_end } } if(object.moving_stopped && test && !active_border_cube && mouse_down){ var same; for(var i = 0; i < border_cube.length; i++){ var r = Math.sqrt(Math.pow(border_cube[i].position.x - object.position.x_end , 2) + Math.pow(border_cube[i].position.z - object.position.y_end , 2)); if(r < 1){ same = true; } } if(!same){ // active_border.push({x: object.position.x_end, y: object.position.y_end}); } } if(object.moving_stopped){ drawLagr(); } if(!test){ drawCross(object.position.x_end, object.position.y_end, "yellow"); } mouse_down = false; if(test){ active_border_cube = null; if(last_selected_border_cube){ last_selected_border_cube.material.color.set( 0x00ff00 ); } } } sea_not_clicked = false; } function onPointerMove(e){ if(mouse_down && active_border_cube){ controls.enableRotate = false; var object = active_border_cube; var canvasBoundingRect = canvas.getBoundingClientRect(); var t = canvasBoundingRect; pointer.x = (e.clientX - t.left) / t.width * 2 - 1, pointer.y = -(e.clientY - t.top) / t.height * 2 + 1; raycaster.setFromCamera( pointer, camera ); const intersects = raycaster.intersectObjects( [floor] ); if(intersects[0] && active_border[object.border_index]){ object.position.x = active_border[object.border_index].x = intersects[0].point.x; object.position.z = active_border[object.border_index].y = intersects[0].point.z; } } } function intersectBorderCube(){ raycaster.setFromCamera( pointer, camera ); const intersects = raycaster.intersectObjects( border_cube_group.children ); for ( let i = 0; i < intersects.length; i ++ ) { if(intersects[i].object.material && intersects[i].object.material.color){ if(intersects[i].object.side == "border_right"){ active_border = border_points[0]; border_right.checked = true; } else{ active_border = border_points[1]; border_left.checked = true; } if(last_selected_border_cube){ if(last_selected_border_cube.side == "border_right"){ last_selected_border_cube.material.color.set( 0x00ff00 ); } else{ last_selected_border_cube.material.color.set( 0x00ffff ); } } intersects[i].object.material.color.set( 0xff0000 ); intersects[i].object.material.opacity = .4; intersects[i].object.material.shading = THREE.SmoothShading; active_border_cube = last_selected_border_cube = intersects[i].object; } } } /////////////////////////////////// Map ///////////////////////////////////// border_points[0] = [ { "x": -1.2113842910829153, "y": -10.20368127403675 }, { "x": -0.3268580714700465, "y": -7.666822126687212 }, { "x": 0.6890313333965716, "y": -4.403478523373602 }, { "x": 2.0357299502829433, "y": -1.4867182714845308 }, { "x": 4.708221202175898, "y": 1.003468923747237 }, { "x": 7.603186470908478, "y": 2.0330304558189978 }, { "x": 9.933313576160806, "y": 1.417204576840065 }, { "x": 11.784359617591125, "y": -0.7930962743302238 } ] active_border = border_points[0]; border_points[1] = [ { "x": 0.37174050726418856, "y": -12.049503723260987 }, { "x": 1.4611625017149312, "y": -11.225539099864234 }, { "x": 3.5639160342243534, "y": -10.928587507175635 }, { "x": 6.383207738717317, "y": -10.026572088951687 }, { "x": 8.288880726504546, "y": -8.838856345464318 }, { "x": 9.423301438191896, "y": -7.014197207129665 }, { "x": 10.628422053686432, "y": -5.119482761193863 }, { "x": 11.764454365474576, "y": -2.9429357450408746 } ]; var rock_active_border = []; function drawMapAndBorders(num){ ctx2.clearRect(0, 0, canvas2.width, canvas2.height); var x, y, angle; var rock_points = light_house.children[0].children[0].children[0].children[0].children[3].geometry.attributes.position; if(rock_active_border.length == 0){ for(var i = 0; i < rock_points.count; i+=5){ if(rock_points.getZ(i) < 0){ x = rock_points.getX(i); y = rock_points.getY(i); angle = getAngle({}, 0, 0, y, x, true).angle; var r = Math.sqrt(Math.pow(x , 2) + Math.pow(y , 2)); var params = getRadiusCoords(0, 0, r, angle + island_group.rotation.y + Math.PI / 2); rock_active_border.push({x: params.x, y: params.y}); } } } for(var i = 0; i < rock_active_border.length; i++){ x = rock_active_border[i].x * light_house.scale.x + island_group.position.x; y = rock_active_border[i].y * light_house.scale.z + island_group.position.z; ctx2.fillStyle = "lime"; ctx2.fillRect(y*10 + 375, -x*10 + 250, 4,4); } ctx2.beginPath(); x = -25; y = - x * Math.cos(Math.PI / 2) / Math.sin(Math.PI / 2); ctx2.strokeStyle = "red"; ctx2.moveTo(y*10 + 375, -x*10 + 250); x = 25; y = - x * Math.cos(Math.PI / 2) / Math.sin(Math.PI / 2); ctx2.lineTo(y*10 + 375, -x*10 + 250); ctx2.stroke(); ctx2.beginPath(); x = -25; y = - x * Math.cos(180 * Math.PI / 180) / Math.sin(180 * Math.PI / 180); ctx2.strokeStyle = "yellow"; ctx2.moveTo(y*10 + 375, -x*10 + 250); x = 25; y = - x * Math.cos(180 * Math.PI / 180) / Math.sin(180 * Math.PI / 180); ctx2.lineTo(y*10 + 375, -x*10 + 250); ctx2.stroke(); var color = ["yellow", "pink", "white", "lime", "blue", "maroon", "purple"]; for(var i = 0; i < active_border.length; i++){ ctx2.fillStyle = "yellow"; ctx2.fillRect(active_border[i].y*10 + 375, -active_border[i].x*10 + 250, 5,5); if(!border_cube[i]){ const w = 0.5; const geometry = new THREE.BoxGeometry( w, w, w ); const material = new THREE.MeshBasicMaterial( {color: 0x00ff00} ); border_cube[i] = new THREE.Mesh( geometry, material ); border_cube[i].position.set(active_border[i].x, w/2, active_border[i].y); border_cube[i].name = border_cube + "_" + i; border_cube[i].border_index = i; border_cube[i].cube_index = i; border_cube[i].side = border_right.id; if(ships[myId].moving_stopped){ border_cube[i].visible = true; } else{ border_cube[i].visible = false; } border_cube_group.add( border_cube[i] ); scene.add( border_cube_group ); } } var k = i; for(var i = 0; i < border_points[1].length; i++){ ctx2.fillStyle = "blue"; ctx2.fillRect(border_points[1][i].y*10 + 375, -border_points[1][i].x*10 + 250, 5,5); if(!border_cube[i + k]){ const w = 0.5; const geometry = new THREE.BoxGeometry( w, w, w ); const material = new THREE.MeshBasicMaterial( {color: 0x00ffff} ); border_cube[i + k] = new THREE.Mesh( geometry, material ); border_cube[i + k].position.set(border_points[1][i].x, w/2, border_points[1][i].y); border_cube[i + k].name = border_cube + "_" + (i + k); border_cube[i + k].border_index = i; border_cube[i + k].cube_index = i + k; border_cube[i + k].side = border_left.id; if(ships[myId].moving_stopped){ border_cube[i + k].visible = true; } else{ border_cube[i + k].visible = false; } border_cube_group.add( border_cube[i + k] ); scene.add( border_cube_group ); } } ctx2.fillStyle = "red"; var radius = 6; ctx2.beginPath(); ctx2.arc(island_group.position.z*10 + 375 - radius/2, -island_group.position.x*10 + 250 - radius/2, radius, 0, Math.PI * 2); ctx2.fill(); for(var num2 = 0; num2 < active_border.length - 1; num2+=1){ var x1, y1, x2, y2; angle = getAngle({}, active_border[num2].y, active_border[num2].x, active_border[num2 + 1].y, active_border[num2 + 1].x, true).angle; ctx2.strokeStyle = "yellow"; ctx2.beginPath(); x1 = active_border[num2].x; y1 = active_border[num2].y; ctx2.moveTo(y1*10 + 375, -x1*10 + 250); x2 = active_border[num2 + 1].x; y2 = - (x2 - active_border[num2].x) * Math.cos(angle) / Math.sin(angle) + active_border[num2].y; ctx2.lineTo(y2*10 + 375, -x2*10 + 250); ctx2.stroke(); } ctx2.fillStyle = "white"; radius = 3; ctx2.beginPath(); ctx2.arc(ships[myId].position.z*10 + 375, -ships[myId].position.x*10 + 250, radius, 0, Math.PI * 2); ctx2.fill(); angle = Math.PI / 2 - ships[myId].rotation.y; ctx2.strokeStyle = "white"; ctx2.beginPath(); x = ships[myId].position.x; y = - (x - ships[myId].position.x) * Math.cos(angle) / Math.sin(angle) + ships[myId].position.z; ctx2.moveTo(y*10 + 375, -x*10 + 250); if(angle < 0 && angle > -Math.PI){ x = -25; } else{ x = 25; } y = - (x - ships[myId].position.x) * Math.cos(angle) / Math.sin(angle) + ships[myId].position.z; ctx2.lineTo(y*10 + 375, -x*10 + 250); ctx2.stroke(); if(ships[myId].before_collision_bezier){ ctx3.fillStyle = "yellow"; } else if(ships[myId].collision){ ctx3.fillStyle = "lightblue"; } else{ ctx3.fillStyle = "red"; } ctx3.fillRect(ships[myId].position.z*10 + 375, -ships[myId].position.x*10 + 250, 2,2); } function drawCross(x0, y0, color, size = 1){ ctx3.lineWidth = 3; var x1, y1, x2, y2; var angle = Math.PI / 4; ctx3.strokeStyle = color; ctx3.beginPath(); x1 = x0 - size; y1 = y0 - size; ctx3.moveTo(y1*10 + 375, -x1*10 + 250); x2 = x0 + size; y2 = y0 + size; ctx3.lineTo(y2*10 + 375, -x2*10 + 250); ctx3.stroke(); var x1, y1, x2, y2; var angle = -Math.PI / 4; ctx3.strokeStyle = color; ctx3.beginPath(); x1 = x0 - size; y1 = y0 + size; ctx3.moveTo(y1*10 + 375, -x1*10 + 250); x2 = x0 + size; y2 = y0 - size; ctx3.lineTo(y2*10 + 375, -x2*10 + 250); ctx3.stroke(); } //////////////////////////////// Water ///////////////////////////////////////////// prom[0] = new Promise((resolve, reject) =>{ const planeSize = 40; // размер плоскости, которую будем использовать для пола var planeGeo = new THREE.PlaneGeometry(planeSize, planeSize, 16, 16); // создаем геометрию для пола — попросту размеры { window.floor = new THREE.Water( planeGeo, { waterNormals: new THREE.TextureLoader().load(floor_texture, function ( texture ) { texture.wrapS = texture.wrapT = THREE.RepeatWrapping; loading_text.innerHTML += "sea is loaded"; resolve(); }), alpha: 0.9, waterColor: 0x1974d2, distortionScale: 0, sunDirection: new THREE.Vector3( 0.0, 0.1, -1.0 ).normalize(), sunColor: 0xffffff, }); floor.position.set(0, 0, 0); floor.rotation.x = Math.PI * -.5; // поворачиваем пол на 90 градусов по X floor.rotation.z = Math.PI * -.5; floor.receiveShadow = true; floor.position.set(0, 0, 0); floor.material.transparent = true; scene.add(floor); // добавляем пол в сцену } }) /////////////////////////////// Particles ///////////////////////////////////////// { partical_material = new THREE.PointsMaterial({ color: "white", map: new THREE.TextureLoader().load(smoke_texture), onBeforeCompile: patch => { patch.uniforms.time = { value: 0 }; patch.vertexShader = particle_vertecies; patch.fragmentShader = particle_fragments; pointsShader = patch; } }) } function updateParticles(){ for(var i in ships){ var object = ships[i]; var id = i; if(object.angle_for_rotation % 10 == 0){ if(object.path && object.path > 0 && object.path < 1 || object.collision){ let sizes = []; let rots = []; let pts = new Array(1000).fill(0).map(p => { sizes.push((Math.random() * 0.5 + 0.1) / 3); rots.push(Math.random() * Math.PI / 10); return new THREE.Vector3().random().subScalar(0.5).multiplyScalar(10) }); if(partical_points[id]){ scene.remove(partical_points[id]); } geometry = new THREE.BufferGeometry().setFromPoints(pts); geometry.setAttribute("size", new THREE.Float32BufferAttribute(sizes, 1)); geometry.setAttribute("rots", new THREE.Float32BufferAttribute(rots, 1)); partical_points[id] = new THREE.Points(geometry, partical_material); partical_points[id].scale.set(0.25, 0.02, 0.1); if(object){ partical_points[id].position.x = object.position.x; partical_points[id].position.z = object.position.z; partical_points[id].rotation.x = object.rotation.x; partical_points[id].rotation.y = object.rotation.y; partical_points[id].rotation.z = object.rotation.z; } scene.add(partical_points[id]); } else{ scene.remove(partical_points[id]); } } } } //////////////////////////////// Models ///////////////////////////////////////////// const gltfLoader = new GLTFLoader(); const fbxLoader = new FBXLoader(); prom[1] = new Promise((resolve, reject) => { gltfLoader.load( ship_model, (gltf) => { resolve(); ships[myId] = gltf.scene; rotor = ships[myId].getObjectByName("Scene").getObjectByName("rotor"); scene.add(ships[myId]); // ships[myId].children[0].receiveShadow = true // ships[myId].children[0].castShadow = true ships[myId].scale.set(0.5, 0.5, 0.5); ships[myId].angle_for_rotation = 0; ships[myId].position.z = ship_init.y; ships[myId].position.x = ship_init.x; ships[myId].rotation.y = ship_init.angle / 180 * Math.PI; ships[myId].move = function(object){shipMove(object)}; var color = colors[myId.toString().slice(-1)]; ships[myId].getObjectByName("Scene").getObjectByName("ship").material.color.set( color ); moving_objects[myId] = ships[myId]; }, (bytes) => { loading_text.innerHTML += " " + "ship model " + Math.floor(bytes.loaded / bytes.total * 100) + "% " + "is loaded"}) }); //////////////////////////////////////////////////////////////////////// prom[2] = new Promise((resolve, reject) => { gltfLoader.load( light_house_model, (gltf) => { resolve(); window.light_house = gltf.scene; light_house.name = "Lighthouse"; light_house.scale.set(0.15, 0.15, 0.15); light_house.position.y -= 0.025; const vertexShader = island_vertecies; const fragmentShader = island_fragments; var rock = light_house.children[0].children[0].children[0].children[0].children[3]; rock.material.onBeforeCompile = patch => { patch.uniforms.texture1 = {value: 0}; patch.uniforms.texture2 = {value: 0}; patch.uniforms.texture3 = {value: 0}; patch.uniforms.intensity = {value: 0}; patch.uniforms.pointShadowMap = {value: 0}; patch.vertexShader= vertexShader; patch.fragmentShader= fragmentShader; window.rockShader = patch; var texture1 = new THREE.TextureLoader().load(rock_texture, function ( texture ) { var texture2 = new THREE.TextureLoader().load(base_path + "/Rock_color2.png", function ( texture ) { var texture3 = new THREE.TextureLoader().load(base_path + "/Rock_color.png", function ( texture ) { window.rockShader.uniforms.texture1.value = texture1; window.rockShader.uniforms.texture2.value = texture2; window.rockShader.uniforms.texture3.value = texture3; window.rockShader.uniforms.intensity.value = 25; map.click(); socket.emit("mouseclick", {x0: ships[myId].position.x, y0: ships[myId].position.z, x: undefined, y: undefined, angle: ships[myId].rotation.y, id: myId}); socket.emit("loaded"); }) }) }); // var img = new Image(); // img.src = rock_texture; // img.onload = function(){createImageBitmap(img).then(function (bitmap) { // // light_house.children[0].children[0].children[0].children[0].children[3].material.map.source.data = bitmap; // setTimeout(function(){ // window.rockShader.uniforms[ 'texture2' ].value = { type: 't', value: 0, texture: new THREE.TextureLoader().loaa( rock_texture ) }; // }, 1000); // })}; }; palmsSet(); island_group.add(light_house); loading_text.innerHTML += " island texture is loading "; }, (bytes) => { loading_text.innerHTML += " " + "light house model " + Math.floor(bytes.loaded / bytes.total * 100) + "% " + "is loaded"}) }); //////////////////////////////////////////////////////////////////////// function palmsSet(){ prom[3] = new Promise((resolve, reject) =>{ gltfLoader.load( palm_model, (gltf) => { resolve(); window.palm = []; palm[0] = gltf.scene; palm[0].name = "Palm"; palm[0].scale.set(0.5, 0.5, 0.5); palm[0].position.x += 5; palm[0].position.z -= 5; palm[0].position.y = 0.4; palm[0].angle_for_rotation = 0; island_group.add(palm[0]); var rock_points = light_house.children[0].children[0].children[0].children[0].children[3].geometry.attributes.position; // light_house.children[0].children[0].children[0].children[0].children[3].receiveShadow = true; // light_house.children[0].children[0].children[0].children[0].children[3].castShadow = true; var rock_points_geometry = light_house.children[0].children[0].children[0].children[0].children[3].geometry; var max = rock_points.count; var index = 0; palm[0].position.x = rock_points.getX(index) * light_house.scale.x; palm[0].position.y = rock_points.getZ(index) * light_house.scale.y; palm[0].position.z = -rock_points.getY(index) * light_house.scale.z; for(var i = 1; i < 50; i++){ palm[i] = palm[0].clone(); // scene.add(palm[i]); island_group.add(palm[i]); index = getRandom(1, max); palm[i].position.x = rock_points.getX(index) * light_house.scale.x; palm[i].position.y = rock_points.getZ(index) * light_house.scale.y; palm[i].position.z = -rock_points.getY(index) * light_house.scale.z; // light_house.children[0].children[0].children[0].children[0].children[2].receiveShadow = true; light_house_borders = light_house.children[0].children[0].children[0].children[0].children[2].geometry.boundingBox; if(palm[i].position.x < (light_house_borders.max.x + 2) * light_house.scale.x && palm[i].position.x > (light_house_borders.min.x - 2) * light_house.scale.x && palm[i].position.z > -(light_house_borders.max.y + 2) * light_house.scale.z && palm[i].position.z < -(light_house_borders.min.y - 2) * light_house.scale.z){ var scale_size = 0.2; } else{ var scale_size = getRandom(30, 50) / 100; } palm[i].scale.set(scale_size, scale_size, scale_size); palm[i].rotation.y = getRandom(0, 314) / 100; palm[i].angle_for_rotation = getRandom(0, 360); // palm[i].children[0].children[0].children[0].children[0].children[1].children[1].children[0].material // = palm[0].children[0].children[0].children[0].children[0].children[1].children[1].children[0].material.clone(); } scene.add(island_group); island_group.position.x += 6; island_group.position.z -= 3; var glass = light_house.getObjectByName("Lighthouse_Glass_0"); glow_sphere.position.y = glass.geometry.boundingSphere.center.z * light_house.scale.y; island_group.add(glow_sphere); let x0 = glass.geometry.boundingSphere.center.x * light_house.scale.x; let y0 = glass.geometry.boundingSphere.center.y * light_house.scale.y; glow_sphere.position.x = x0; glow_sphere.position.z = -y0; island_group.rotation.y = -Math.PI / 3; loading_text.style.display= "none"; window.palmShader = []; for(let i = 0; i < palm.length; i++){ // let потому что дальше промисс и значение придет, когда цикл отработает palm[i].children[0].children[0].children[0].children[0].children[1].children[1].children[0].material.onBeforeCompile = patch => { patch.uniforms.uTime = { value: 0 }; // console.log(patch.vertexShader); patch.vertexShader = palm_vertecies; // console.log(patch.fragmentShader); patch.fragmentShader = palm_fragments; window.palmShader[i] = patch; }; } }, (bytes) => { loading_text.innerHTML += " " + "palm models " + Math.floor(bytes.loaded / bytes.total * 100) + "% " + "is loaded"}) }); } //////////////////////////////////////////////////////////////////////// prom[4] = new Promise((resolve, reject) =>{ fbxLoader.load( base_path + "/Walking.fbx", (fbx) => { resolve(); const root = fbx; root.scale.set(0.0025, 0.0025, 0.0025); island_group.add(root); man_pers = fbx; man_pers.angle_for_rotation = 0; const animations = man_pers.animations; man_pers.mixer = new THREE.AnimationMixer( man_pers ); man_pers.step = 0.6; man_pers.mixer.timeScale = 1; man_pers.clock = new THREE.Clock(); const walkAction = man_pers.mixer.clipAction( animations[ 0 ] ); walkAction.play(); }, (bytes) => { loading_text.innerHTML += " " + "character model " + Math.floor(bytes.loaded / bytes.total * 100) + "% " + "is loaded"}) }) //////////////////////////////////////////////////////////////////////// prom[5] = new Promise((resolve, reject) => { fbxLoader.load( base_path + "/blue_whale.fbx", (fbx) => { resolve(); var id = Date.now(); whale = moving_objects[id] = fishes[id] = fbx; scene.add(whale); whale.scale.set(0.001, 0.001, 0.001); whale.angle_for_rotation = 0; whale.rotation_step = 0.1; whale.move = function(object){fishMove(object)}; const animations = fbx.animations; whale.mixer = new THREE.AnimationMixer( whale ); whale.step = 0.002; whale.mixer.timeScale = 5; whale.clock = new THREE.Clock(); const walkAction = whale.mixer.clipAction( animations[ 0 ] ); walkAction.play(); }, (bytes) => { loading_text.innerHTML += " " + "whale model " + Math.floor(bytes.loaded / bytes.total * 100) + "% " + "is loaded"}) }); //////////////////////////////////////////////////////////////////////// prom[6] = new Promise((resolve, reject) => { gltfLoader.load( base_path + "/seagull.glb", (gltf) => { resolve(); var id = Date.now(); seagull = birds[id] = moving_objects[id] = gltf.scene; scene.add(seagull); var size = 0.05; seagull.scale.set(size, size, size); seagull.angle_for_rotation = 0; seagull.move = function(object){fishMove(object, 5, 10)}; const animations = gltf.animations; seagull.mixer = new THREE.AnimationMixer( seagull ); seagull.step = 0.002; seagull.mixer.timeScale = 5; seagull.clock = new THREE.Clock(); var walkAction = seagull.mixer.clipAction( animations[ 0 ] ); walkAction.play(); for(var i = 0; i <= 2; i++){ id = Date.now() + "_" + i; var bird = birds[id] = moving_objects[id] = SkeletonUtils.clone(seagull); scene.add(bird); bird.scale.set(size, size, size); bird.angle_for_rotation = 0; bird.move = function(object){fishMove(object, 5, 10)}; bird.position.set(getRandom(-20, 20), getRandom(-20, 20), getRandom(-20, 20)); bird.mixer = new THREE.AnimationMixer( bird ); bird.step = 0.002; bird.mixer.timeScale = 5; bird.clock = new THREE.Clock(); walkAction = bird.mixer.clipAction( animations[ 0 ] ); walkAction.play(); } }, (bytes) => { loading_text.innerHTML += " " + "seagull model " + Math.floor(bytes.loaded / bytes.total * 100) + "% " + "is loaded"}) }); //////////////////////////////////////////////////////////////////////// Promise.all(prom).then((values) => { loading_text.style.display= "none"; renderer.setAnimationLoop( animateAll ); scene.add( glow_sphere ); }); function animateAll() { animatePers(); animateFish(); animateBird(); } function animatePers() { let mixerUpdateDelta = man_pers.clock.getDelta(); man_pers.mixer.update( mixerUpdateDelta ); } function animateFish() { let mixerUpdateDelta = whale.clock.getDelta(); whale.mixer.update( mixerUpdateDelta ); } function animateBird() { for(var i in birds){ bird = birds[i]; let mixerUpdateDelta = bird.clock.getDelta(); bird.mixer.update( mixerUpdateDelta ); } } const geometry2 = new THREE.SphereGeometry( 1, 32, 16 ); const glow_sphere = new THREE.Mesh( geometry2, new THREE.MeshStandardMaterial() ); let glowMaterial = new THREE.ShaderMaterial({ uniforms: { viewVector: { type: "v3", value: camera.position } }, vertexShader: ` uniform vec3 viewVector; varying float intensity; void main() { gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4( position, 1.0 ); vec3 actual_normal = vec3(modelMatrix * vec4(normal, 0.0)); intensity = pow( dot(normalize(viewVector), actual_normal), 6.0 ); } `, fragmentShader: ` varying float intensity; void main() { vec3 glow = vec3(1, 0.8, 0.1) * intensity; gl_FragColor = vec4( glow, 1.0 ); } `, side: THREE.FrontSide, blending: THREE.AdditiveBlending, transparent: true }); glow_sphere.material = glowMaterial;