From fd6478677811c48d6824a4d8de4428745780deb0 Mon Sep 17 00:00:00 2001 From: JannisX11 Date: Sun, 12 Sep 2021 15:09:01 +0200 Subject: [PATCH] Add face loop select Fix outliner scrolling issue Fix #1043 keyframes can be created at invalid timecodes Fix #1040 Fix vertex snap issues (#1017) Add "Add Texture Mesh" to edit menu Change default animated texture FPS to 7 Fix #1012 Uv window does not refresh scale when changing project res --- js/animations/animation.js | 2 +- js/interface/menu.js | 1 + js/interface/settings.js | 2 +- js/io/project.js | 1 + js/outliner/mesh.js | 40 +++++++++++++++++++++++++++---- js/outliner/outliner.js | 5 ++-- js/outliner/texture_mesh.js | 16 +++++++++++++ js/preview/preview.js | 47 +++++++++++++++++++++++++++++++------ js/transform.js | 29 +++++++++++++++-------- 9 files changed, 117 insertions(+), 26 deletions(-) diff --git a/js/animations/animation.js b/js/animations/animation.js index 0496b472..5e3d0758 100644 --- a/js/animations/animation.js +++ b/js/animations/animation.js @@ -728,7 +728,7 @@ class GeneralAnimator { } keyframe.channel = channel; - keyframe.time = time; + keyframe.time = Timeline.snapTime(time); this[channel].push(keyframe); keyframe.animator = this; diff --git a/js/interface/menu.js b/js/interface/menu.js index 193219f0..270ba6ca 100644 --- a/js/interface/menu.js +++ b/js/interface/menu.js @@ -579,6 +579,7 @@ const MenuBar = { 'add_group', 'add_locator', 'add_null_object', + 'add_texture_mesh', '_', 'duplicate', 'rename', diff --git a/js/interface/settings.js b/js/interface/settings.js index 68b9d13e..cf576dc3 100644 --- a/js/interface/settings.js +++ b/js/interface/settings.js @@ -220,7 +220,7 @@ const Settings = { Canvas.updateRenderSides(); }}); new Setting('background_rendering', {category: 'preview', value: true}); - new Setting('texture_fps', {category: 'preview', value: 2, type: 'number', onChange() { + new Setting('texture_fps', {category: 'preview', value: 7, type: 'number', onChange() { TextureAnimator.updateSpeed() }}); new Setting('particle_tick_rate',{category: 'preview', value: 30, type: 'number', onChange() { diff --git a/js/io/project.js b/js/io/project.js index 776fe5b9..9bf7e98c 100644 --- a/js/io/project.js +++ b/js/io/project.js @@ -417,6 +417,7 @@ function setProjectResolution(width, height, modify_uv) { function updateProjectResolution() { if (Interface.Panels.uv) { UVEditor.vue.project_resolution.replace([Project.texture_width, Project.texture_height]); + UVEditor.vue.updateSize() } if (Texture.selected) { // Update animated textures diff --git a/js/outliner/mesh.js b/js/outliner/mesh.js index fa8853f0..562edc9c 100644 --- a/js/outliner/mesh.js +++ b/js/outliner/mesh.js @@ -50,7 +50,7 @@ class MeshFace extends Face { } invert() { if (this.vertices.length < 3) return this; - [this.vertices[1], this.vertices[2]] = [this.vertices[2], this.vertices[1]]; + [this.vertices[0], this.vertices[1]] = [this.vertices[1], this.vertices[0]]; } isSelected() { let selected_vertices = Project.selected_vertices[this.mesh.uuid]; @@ -86,6 +86,35 @@ class MeshFace extends Face { } return vertices; } + getAdjacentFace(side_index = 0) { + let vertices = this.getSortedVertices(); + side_index = side_index % this.vertices.length; + let side_vertices = [ + vertices[side_index], + vertices[side_index+1] || vertices[0] + ] + for (let fkey in this.mesh.faces) { + let face = this.mesh.faces[fkey]; + if (face === this) continue; + if (face.vertices.includes(side_vertices[0]) && face.vertices.includes(side_vertices[1])) { + let f_vertices = face.getSortedVertices(); + let index_a = f_vertices.indexOf(side_vertices[0]); + let index_b = f_vertices.indexOf(side_vertices[1]); + if (index_b - index_a == -1 || (index_b - index_a == f_vertices.length-1)) { + return { + face, + index: index_b + } + } + } + } + return null; + } + getFaceKey() { + for (let fkey in this.mesh.faces) { + if (this.mesh.faces[fkey] == this) return fkey; + } + } } new Property(MeshFace, 'array', 'vertices', {default: 0}); @@ -235,7 +264,8 @@ class Mesh extends OutlinerElement { el.uuid = this.uuid return el; } - getSelectedVertices() { + getSelectedVertices(make) { + if (make && !Project.selected_vertices[this.uuid]) Project.selected_vertices[this.uuid] = []; return Project.selected_vertices[this.uuid] || []; } getSelectedFaces() { @@ -546,9 +576,9 @@ new NodePreviewController(Mesh, { let sorted_vertices = face.getSortedVertices(); - indices.push(index_offset + 0); - indices.push(index_offset + 1); - indices.push(index_offset + 2); + indices.push(face_indices[sorted_vertices[0]]); + indices.push(face_indices[sorted_vertices[1]]); + indices.push(face_indices[sorted_vertices[2]]); indices.push(face_indices[sorted_vertices[0]]); indices.push(face_indices[sorted_vertices[2]]); indices.push(face_indices[sorted_vertices[3]]); diff --git a/js/outliner/outliner.js b/js/outliner/outliner.js index b74aed75..fb67c4e0 100644 --- a/js/outliner/outliner.js +++ b/js/outliner/outliner.js @@ -1349,6 +1349,7 @@ Interface.definePanels(function() { list.scrollTop += (mouse_pos.y - (list_offset.top + list.clientHeight)) / 6 + 3; } } + let scrollIntervalID; function move(e2) { convertTouchEvent(e2); @@ -1387,7 +1388,7 @@ Interface.definePanels(function() { } document.body.append(helper); - setInterval(scrollInterval, 1000/60) + scrollIntervalID = setInterval(scrollInterval, 1000/60) } helper.style.left = `${e2.clientX}px`; helper.style.top = `${e2.clientY}px`; @@ -1412,7 +1413,7 @@ Interface.definePanels(function() { } function off(e2) { if (helper) helper.remove(); - clearInterval(scrollInterval); + clearInterval(scrollIntervalID); removeEventListeners(document, 'mousemove touchmove', move); removeEventListeners(document, 'mouseup touchend', off); $('.drag_hover').removeClass('drag_hover'); diff --git a/js/outliner/texture_mesh.js b/js/outliner/texture_mesh.js index 48e3fe50..8348d4b0 100644 --- a/js/outliner/texture_mesh.js +++ b/js/outliner/texture_mesh.js @@ -23,6 +23,22 @@ class TextureMesh extends OutlinerElement { } return pos; } + moveVector(arr, axis, update = true) { + if (typeof arr == 'number') { + var n = arr; + arr = [0, 0, 0]; + arr[axis||0] = n; + } else if (arr instanceof THREE.Vector3) { + arr = arr.toArray(); + } + arr.forEach((val, i) => { + this.origin[i] += val; + }) + if (update) { + this.preview_controller.updateTransform(this); + } + TickUpdates.selection = true; + } extend(object) { for (var key in TextureMesh.properties) { TextureMesh.properties[key].merge(this, object) diff --git a/js/preview/preview.js b/js/preview/preview.js index a39a8e46..04c19622 100644 --- a/js/preview/preview.js +++ b/js/preview/preview.js @@ -417,7 +417,8 @@ class Preview { element, intersects, intersect, - vertex + vertex, + vertex_index: intersect.index, } } else if (intersect_object.type == 'LineSegments') { var element = OutlinerNode.uuids[intersect_object.parent.name]; @@ -733,13 +734,45 @@ class Preview { if (data.element instanceof Mesh && select_mode == 'face') { if (!data.element.selected) data.element.select(event); - if (!Project.selected_vertices[data.element.uuid]) { - Project.selected_vertices[data.element.uuid] = []; + let mesh = data.element; + let selected_vertices = mesh.getSelectedVertices(true); + + if (event.altKey || Pressing.overrides.alt) { + + let mesh = data.element; + let start_face = mesh.faces[data.face]; + if (!start_face) return; + let processed_faces = []; + function selectFace(face, index) { + if (processed_faces.includes(face)) return; + processed_faces.push(face); + let next = face.getAdjacentFace(index); + if (next) selectFace(next.face, next.index+2); + + } + + let face_test = start_face.getAdjacentFace(0); + let index = (face_test && face_test.face.isSelected()) ? 1 : 0; + selectFace(start_face, index); + + if (!(event.ctrlOrCmd || Pressing.overrides.ctrl || event.shiftKey || Pressing.overrides.shift)) { + selected_vertices.empty(); + UVEditor.vue.selected_faces.empty(); + } + + processed_faces.forEach(face => { + Project.selected_vertices[data.element.uuid].safePush(...face.vertices); + let fkey = face.getFaceKey(); + UVEditor.vue.selected_faces.push(fkey); + }); + } else { + if (!(event.ctrlOrCmd || Pressing.overrides.ctrl || event.shiftKey || Pressing.overrides.shift)) { + selected_vertices.empty(); + UVEditor.vue.selected_faces.empty(); + } + Project.selected_vertices[data.element.uuid].safePush(...data.element.faces[data.face].vertices); + UVEditor.vue.selected_faces.safePush(data.face); } - if (!(event.ctrlOrCmd || Pressing.overrides.ctrl || event.shiftKey || Pressing.overrides.shift)) { - Project.selected_vertices[data.element.uuid].empty(); - } - Project.selected_vertices[data.element.uuid].safePush(...data.element.faces[data.face].vertices); } else { data.element.select(event) diff --git a/js/transform.js b/js/transform.js index d9dcf011..6ee88a65 100644 --- a/js/transform.js +++ b/js/transform.js @@ -226,20 +226,25 @@ const Vertexsnap = { } vectors.push([0, 0, 0]); - let points = new THREE.Points(new THREE.BufferGeometry(), Canvas.meshVertexMaterial); + let points = new THREE.Points(new THREE.BufferGeometry(), new THREE.PointsMaterial().copy(Canvas.meshVertexMaterial)); points.vertices = vectors; let vector_positions = []; vectors.forEach(vector => vector_positions.push(...vector)); + let vector_colors = []; + vectors.forEach(vector => vector_colors.push(gizmo_colors.grid.r, gizmo_colors.grid.g, gizmo_colors.grid.b)); points.geometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array(vector_positions), 3)); - points.geometry.setAttribute('color', new THREE.Float32BufferAttribute(new Array(vectors.length).fill(0), 1)); + points.geometry.setAttribute('color', new THREE.Float32BufferAttribute(new Float32Array(vector_colors), 3)); + points.material.transparent = true; mesh.vertex_points = points; mesh.outline.add(points); } mesh.vertex_points.visible = true; + mesh.vertex_points.renderOrder = 900; Vertexsnap.elements_with_vertex_gizmos.push(element) }, clearVertexGizmos: function() { + Project.model_3d.remove(Vertexsnap.line); Vertexsnap.elements_with_vertex_gizmos.forEach(element => { if (element.mesh.vertex_points) { element.mesh.vertex_points.visible = false; @@ -263,13 +268,14 @@ const Vertexsnap = { let colors = []; for (let i = 0; i < points.geometry.attributes.position.count; i++) { let color; - if (data && data.type == 'vertex' && data.intersect.index == i) { + if (data && data.element == el && data.type == 'vertex' && data.vertex_index == i) { color = gizmo_colors.outline; } else { color = gizmo_colors.grid; } colors.push(color.r, color.g, color.b); } + points.material.depthTest = !(data.element == el); points.geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3)); }) } @@ -309,7 +315,9 @@ const Vertexsnap = { if (Vertexsnap.step1) { Vertexsnap.step1 = false Vertexsnap.vertex_pos = Vertexsnap.getGlobalVertexPos(data.element, data.vertex); - Vertexsnap.elements = Outliner.selected.slice() + Vertexsnap.vertex_index = data.vertex_index; + Vertexsnap.move_origin = typeof data.vertex !== 'string' && data.vertex.allEqual(0); + Vertexsnap.elements = Outliner.selected.slice(); Vertexsnap.clearVertexGizmos() $('#preview').css('cursor', (Vertexsnap.step1 ? 'copy' : 'alias')) @@ -330,13 +338,13 @@ const Vertexsnap = { let mode = BarItems.vertex_snap_mode.get() - if (Vertexsnap.vertex_id === 100) { + if (Vertexsnap.move_origin) { Vertexsnap.elements.forEach(function(element) { let vec = Vertexsnap.getGlobalVertexPos(data.element, data.vertex); if (Format.bone_rig && element.parent instanceof Group && element.mesh.parent) { - //element.mesh.parent.worldToLocal(vec); + element.mesh.parent.worldToLocal(vec); } let vec_array = vec.toArray() vec_array.V3_add(element.parent.origin); @@ -351,7 +359,7 @@ const Vertexsnap = { //Scale var m; - switch (Vertexsnap.vertex_id) { + switch (Vertexsnap.vertex_index) { case 0: m=[ 1,1,1 ]; break; case 1: m=[ 1,1,0 ]; break; case 2: m=[ 1,0,1 ]; break; @@ -361,6 +369,7 @@ const Vertexsnap = { case 6: m=[ 0,0,0 ]; break; case 7: m=[ 0,0,1 ]; break; } + console.log() Vertexsnap.elements.forEach(function(obj) { var q = obj.mesh.getWorldQuaternion(new THREE.Quaternion()).invert() @@ -385,7 +394,7 @@ const Vertexsnap = { var q = obj.mesh.parent.getWorldQuaternion(new THREE.Quaternion()).invert(); cube_pos.applyQuaternion(q); } - if (Format.rotate_cubes) { + if (obj instanceof Cube && Format.rotate_cubes) { obj.origin.V3_add(cube_pos); } var in_box = obj.moveVector(cube_pos.toArray()); @@ -646,7 +655,7 @@ function moveElementsInSpace(difference, axis) { } } else if (space instanceof Group) { - if (el.movable) el.from[axis] += difference; + if (el.movable && el instanceof Mesh == false) el.from[axis] += difference; if (el.resizable && el.to) el.to[axis] += difference; if (el.rotatable && el instanceof Locator == false) el.origin[axis] += difference; } else { @@ -677,7 +686,7 @@ function moveElementsInSpace(difference, axis) { } } - if (el.movable) el.from.V3_add(m.x, m.y, m.z); + if (el.movable && el instanceof Mesh == false) el.from.V3_add(m.x, m.y, m.z); if (el.resizable && el.to) el.to.V3_add(m.x, m.y, m.z); if (move_origin) { if (el.rotatable && el instanceof Locator == false && el instanceof TextureMesh == false) el.origin.V3_add(m.x, m.y, m.z);