{"id":355315,"date":"2024-05-20T23:06:18","date_gmt":"2024-05-20T23:06:18","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=355315"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=355315","title":{"rendered":"<span>\u041b\u0435\u0442\u0430\u0434\u043b\u043e, \u043f\u0440\u0435\u0434\u0438\u0441\u043b\u043e\u0432\u0438\u0435<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u042d\u0442\u0430 \u0441\u0442\u0430\u0442\u044c\u044f, \u043c\u043e\u0439 \u043a\u043e\u043d\u0441\u043f\u0435\u043a\u0442, \u0441\u0438\u0433\u043d\u0430\u043b\u044c\u043d\u044b\u0439 \u0444\u043b\u0430\u0433, \u0438\u043b\u0438 \u043e\u0447\u0435\u0440\u0435\u0434\u043d\u0430\u044f \u0442\u0440\u0435\u043d\u0438\u0440\u043e\u0432\u043a\u0430 \u0438\u0437\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0441\u0432\u043e\u0438\u0445 \u043c\u044b\u0441\u043b\u0435\u0439? \u0412 \u0441\u0438\u043b\u0443 \u043e\u0431\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u0441\u0442\u0432\u0430, \u043f\u0440\u0438\u043a\u043e\u0441\u043d\u0443\u043b\u0441\u044f \u043a unreal, \u0437\u0430\u043c\u0435\u0447\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u0432 &#171;\u0443\u043c\u0435\u043b\u044b\u0445 \u0440\u0443\u043a\u0430\u0445&#187;, \u043c\u043d\u043e\u0433\u043e \u043e\u0434 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u043e \u0441\u0435\u043c\u0443 \u0442\u0432\u043e\u0440\u0435\u043d\u0438\u044e \u0447\u0435\u043b\u043e\u0432\u0435\u0447\u0435\u0441\u043a\u043e\u0439 \u043c\u044b\u0441\u043b\u0438, \u0442\u0430\u043a \u0447\u0442\u043e \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u0441 \u043d\u0438\u043c \u0431\u043e\u043b\u044c\u0448\u0430\u044f \u0447\u0435\u0441\u0442\u044c \u0434\u043b\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430. \u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0438\u0433\u0440, \u0432\u0438\u0437\u0443\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f, \u0438\u0441\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u043d\u0438\u044f, \u043c\u043d\u043e\u0433\u043e \u0432\u0441\u0435\u0433\u043e \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e\u0433\u043e \u0437\u0430\u043b\u043e\u0436\u0435\u043d\u043e \u0432 \u044d\u0442\u043e\u0442 \u043f\u0440\u043e\u0435\u043a\u0442 \u0441 \u043c\u043d\u043e\u0433\u043e\u043b\u0435\u0442\u043d\u0435\u0439 \u0438\u0441\u0442\u043e\u0440\u0438\u0435\u0439 \u0440\u0430\u0437\u0432\u0438\u0442\u0438\u044f. \u041e\u0442\u043a\u0440\u044b\u0442\u043e\u0441\u0442\u044c \u0438 \u0431\u043e\u043b\u044c\u0448\u043e\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u0441\u0442\u0432\u043e, \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u043f\u043e\u043d\u0438\u0436\u0430\u0435\u0442 \u043f\u043e\u0440\u043e\u0433 \u0432\u0445\u043e\u0436\u0434\u0435\u043d\u0438\u044f, \u043a\u043e\u043d\u0435\u0447\u043d\u043e \u0442\u0440\u0438\u0432\u0438\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u043f\u0438\u0441\u0430\u0442\u044c \u0442\u0430\u043a\u043e\u0435, \u043a\u0430\u0436\u0434\u044b\u0439 \u0432\u0442\u043e\u0440\u043e\u0439 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u0441 \u0442\u0430\u043a\u0438\u043c\u0438 \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a\u0430\u043c\u0438, \u043d\u043e \u044d\u0442\u043e \u0433\u043e\u0432\u043e\u0440\u0438\u0442 \u043e \u043e\u0431\u0449\u0435\u0439 \u0432\u044b\u0441\u043e\u043a\u043e\u0439 \u043f\u043b\u0430\u043d\u043a\u0435 \u043d\u044b\u043d\u0435\u0448\u043d\u0438\u0445 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u0434\u043b\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043b\u044e\u0431\u044b\u0445 \u0442\u0435\u0445\u043d\u043e \u0438\u0437\u0432\u0440\u0430\u0449\u0451\u043d\u043d\u044b\u0445 \u0444\u0430\u043d\u0442\u0430\u0437\u0438\u0439. \u041d\u0435\u0432\u0435\u0440\u043e\u044f\u0442\u043d\u043e\u0435 \u0441\u0442\u0435\u0447\u0435\u043d\u0438\u0435 \u043e\u0431\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u0441\u0442\u0432, \u043f\u043e\u043b\u0443\u0447\u0430\u044e \u0434\u0435\u043d\u044c\u0433\u0438 \u0437\u0430 \u0442\u043e \u0447\u0442\u043e \u0443\u0447\u0443\u0441\u044c \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c \u0441 \u044d\u0442\u0438\u043c \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u043c. <\/p>\n<h3>\u041a\u043e\u0440\u043e\u0442\u043a\u043e \u043e \u0437\u0430\u0434\u0430\u0447\u0435: <\/h3>\n<ol>\n<li>\n<p>\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0444\u0430\u0439\u043b \u0444\u043e\u0442\u043e\u0433\u0440\u0430\u043c\u043c\u0435\u0442\u0440\u0438\u0438 \u043c\u0435\u0441\u0442\u043d\u043e\u0441\u0442\u0438<\/p>\n<\/li>\n<li>\n<p>\u041d\u0430\u0447\u0435\u0440\u0442\u0438\u0442\u044c \u043f\u0443\u0442\u044c<\/p>\n<\/li>\n<li>\n<p>\u041f\u043e \u0437\u0430\u0434\u0430\u043d\u043d\u043e\u043c\u0443 \u043f\u0443\u0442\u0438 \u0434\u0432\u0438\u0433\u0430\u0442\u044c \u043a\u0430\u043c\u0435\u0440\u0443<\/p>\n<\/li>\n<li>\n<p>\u0421\u043e\u0445\u0440\u0430\u043d\u044f\u0442\u044c \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0441 \u043a\u0430\u043c\u0435\u0440\u044b<\/p>\n<\/li>\n<\/ol>\n<blockquote>\n<p>\u0413\u043b\u0430\u0432\u043d\u043e\u0435 \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u0435: \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0431\u043e\u043b\u044c\u0448\u0443\u044e \u0447\u0430\u0441\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u0430 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435\u043c C++, \u0441 \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c blueprint<\/p>\n<\/blockquote>\n<p>\u0418\u0437\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e \u044f \u0441\u0434\u0435\u043b\u0430\u043b \u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u043f\u0440\u043e\u0442\u043e\u0442\u0438\u043f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f tkinter (python) \u0434\u043b\u044f \u0432\u0438\u0437\u0443\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0441\u0432\u043e\u0438\u0445 \u043c\u044b\u0441\u043b\u0435\u0439, \u0432\u043c\u0435\u0441\u0442\u043e \u0444\u0430\u0439\u043b\u0430 \u0444\u043e\u0442\u043e\u0433\u0440\u0430\u043c\u043c\u0435\u0442\u0440\u0438\u0438, \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/3a9\/268\/773\/3a9268773bad8ca5eab06a5dc13992ca.gif\" alt=\"\u043f\u0440\u043e\u0441\u0442\u0430\u044f \u0432\u0438\u0437\u0443\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f\" title=\"\u043f\u0440\u043e\u0441\u0442\u0430\u044f \u0432\u0438\u0437\u0443\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f\" width=\"800\" height=\"335\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/3a9\/268\/773\/3a9268773bad8ca5eab06a5dc13992ca.gif\"\/><\/p>\n<div><figcaption>\u043f\u0440\u043e\u0441\u0442\u0430\u044f \u0432\u0438\u0437\u0443\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f<\/figcaption><\/div>\n<\/figure>\n<details class=\"spoiler\">\n<summary>\u041a\u043e\u0434 \u0432\u0438\u0437\u0443\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">from tkinter import * from tkinter import filedialog from tkinter import messagebox from tkinter import ttk from PIL import Image, ImageTk import os import random import numpy as np import matplotlib.pyplot as plt import matplotlib.image as mpimg import cv2 import math import time   width_window = 300.0 height_window = 200.0   def crop_rect(img, rect, ix):     print(\"rect!\")     # \u043f\u043e\u0432\u043e\u0440\u043e\u0442 \u0444\u0440\u0430\u0433\u043c\u0435\u043d\u0442\u0430     center, size, angle = rect[0], rect[1], rect[2]     center, size = tuple(map(int, center)), tuple(map(int, size))     height, width = img.shape[0], img.shape[1]     M = cv2.getRotationMatrix2D(center, angle, 1)     img_rot = cv2.warpAffine(img[:,:,:3], M, (width, height))     cut_img = cv2.getRectSubPix(img_rot, size, center)          # \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0447\u0430\u0441\u0442\u044c \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f\/\u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440 \u0432\u044b\u0440\u0435\u0437\u0430\u043d\u043d\u043e\u0433\u043e \u0444\u0440\u0430\u0433\u043c\u0435\u043d\u0442\u0430     cv2.imwrite(f'data\/cut_parts\/{ix}_part.jpg', cut_img)     cv2.imshow('cut_img', cut_img)     time.sleep(0.08)     if cv2.waitKey(1) == ord('q'):         cv2.destroyAllWindows()     return img   class CutTool():     def __init__(self, master):         # \u043d\u0430\u0439\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0433\u043e \u043e\u043a\u043d\u0430         self.parent = master         self.parent.title(\"CutTool\")         self.frame = Frame(self.parent)         self.frame.pack(fill=BOTH, expand=1)         self.parent.resizable(width = True, height = True)          # \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u043a\u0443\u0440\u0441\u043e\u0440\u0430 \u043c\u044b\u0448\u0438         self.STATE = {}         self.STATE['click'] = 0         self.STATE['x'], self.STATE['y'] = 0, 0          # \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b          self.coordList = []         self.running = False                  self.image_container = False                  self.step_size = 20         # ----------------- GUI stuff ---------------------          # \u0433\u043b\u0430\u0432\u043d\u0430\u044f \u043f\u0430\u043d\u0435\u043b\u044c \u0434\u043b\u044f \u043c\u0430\u0440\u043a\u0438\u0440\u043e\u0432\u043a\u0438         self.mainPanel = Canvas(self.frame, cursor='tcross')         self.mainPanel.bind('&lt;ButtonPress-1>',self.start_motor)         self.mainPanel.bind('&lt;ButtonRelease-1>',self.stop_motor)                           self.mainPanel.bind(\"&lt;Motion>\", self.mouseMove)         self.mainPanel.grid(row = 2, column = 1, rowspan = 4, sticky = W+N)          # \u043f\u0430\u043d\u0435\u043b\u044c \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u043d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u0438 \u043f\u043e \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f\u043c         self.ctrPanel = Frame(self.frame)         self.ctrPanel.grid(row = 6, colum\u043c\u044b\u0448\u0438n = 1, columnspan = 2, sticky = W+E)          # \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043a\u0443\u0440\u0441\u043e\u0440\u0430 \u043c\u044b\u0448\u0438         self.disp = Label(self.ctrPanel, text='')         self.disp.pack(side = RIGHT)                  self.loadImage()              def mouseClick(self, event):         if self.STATE['click'] == 0:             self.STATE['x'], self.STATE['y'] = event.x, event.y         print (\"mouseClick event --->\", self.STATE)      def mouseMove(self, event):         if self.running:             self.draw(event)         self.disp.config(text = 'x: %d, y: %d' %(event.x, event.y))      def start_motor(self, event):         self.running = True         print(\"starting motor...\")          def stop_motor(self, event):         print(\"stopping motor...\")         self.running = False         self.curve_2d(self.coordList)         self.coordList = []      def curve_2d(self, x):         R = len(x)         pts = x         x = np.array(x)         ptdiff = lambda p1, p2: (p1[0]-p2[0], p1[1]-p2[1])          diffs = (ptdiff(p1, p2) for p1, p2 in zip (pts, pts[1:]))         path = sum(math.hypot(*d) for d in  diffs)          img_array = np.array(self.img)         img_array2 = np.array(self.img)         print (img_array.shape)         global img_         for ix, i in enumerate(pts):             if ix == 0: continue             if ix % self.step_size == 0:                                      \"\"\"                 1 \u0443\u0437\u043d\u0430\u0442\u044c \u0443\u0433\u043e\u043b \u043c\u0435\u0436\u0434\u0443 \u043d\u0430\u0441\u0442\u043e\u044f\u0449\u0435\u0439 \u0442\u043e\u0447\u043a\u043e\u0439 \u0438 \u043f\u0440\u043e\u0448\u043b\u043e\u0439 \u043f\u043e \u043e\u0442\u043d\u043e\u0448\u0435\u043d\u0438\u044e \u043a \u043e\u0441\u0438 'x'                 2 \u043f\u0440\u043e\u0432\u0435\u0440\u043d\u0443\u0442\u044c \u043d\u0430 \u044d\u0442\u043e\u0442 \u0443\u0433\u043e\u043b \u0442\u043e\u0447\u043a\u0438 \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u043f\u0440\u044f\u043c\u043e\u0443\u0433\u043e\u043b\u044c\u043d\u0438\u043a\u0430                  \"\"\"                 a = np.array(pts[ix])                 b = np.array(pts[ix-self.step_size])                  ab = a - b                 hypotenuse = np.hypot(ab[0], ab[1])                        cos_A = ab[0]\/hypotenuse                 angle = int(math.degrees(math.acos(cos_A)))                    # \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e\u0435 \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435                              if ab[1] &lt; 0:                     angle = -float(angle)                 else:                     angle = float(angle)                 rect_ = ((float(a[0]), float(a[1])), (height_window, width_window), angle)                 img_array2_ = crop_rect(img_array, rect_, ix)                 # \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u043b\u0438\u043d\u0438\u0438                 box = cv2.boxPoints(rect_)                 box = np.int0(box)                 cv2.drawContours(img_array2, [box], 0, (0,0,255), 2)                 cv2.circle(img_array2, (a[0], a[1]), radius=10, color=(0, 0, 255), thickness=-1)                                img_ = ImageTk.PhotoImage(image=Image.fromarray(img_array2))                 self.mainPanel.itemconfig(self.image_container, image=img_)                   # \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u044c \u043a\u0430\u043d\u0432\u0430\u0441                 self.mainPanel.update()                                 def loadImage(self):         # \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435         self.img = Image.open(\"media-info\/test.png\")         size = self.img.size         self.factor = max(size[0]\/10000., size[1]\/10000., 1.)         self.img = self.img.resize((int(size[0]\/self.factor), int(size[1]\/self.factor)))         print (int(size[0]\/self.factor), int(size[1]\/self.factor))         self.tkimg = ImageTk.PhotoImage(self.img)         self.mainPanel.config(width = max(self.tkimg.width(), 400), height = max(self.tkimg.height(), 400))         self.image_container = self.mainPanel.create_image(0, 0, image = self.tkimg, anchor=NW)              def draw(self, event):         self.coordList.append((event.x, event.y))         self.mainPanel.create_oval(event.x - 3,                                   event.y - 3,                                   event.x + 3,                                   event.y + 3,                                   fill=\"red\", outline=\"red\")           if __name__ == '__main__':     root = Tk()     tool = CutTool(root)     root.resizable(width =  True, height = True)     root.mainloop()<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p><strong>\u041f\u043b\u0430\u043d \u0435\u0441\u0442\u044c, \u0432\u0438\u0437\u0443\u0430\u043b\u044c\u043d\u044b\u0439 \u043e\u0440\u0438\u0435\u043d\u0442\u0438\u0440 \u0435\u0441\u0442\u044c, \u0432\u0440\u0435\u043c\u044f \u043f\u0440\u0438\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0439!<\/strong> \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 unreal 4.27 \u0437\u0430\u043d\u044f\u043b\u0430 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0434\u043d\u0435\u0439 \u0438\u0437-\u0437\u0430 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0439 \u043d\u0430\u043b\u043e\u0436\u0435\u043d\u043d\u044b\u0445 epic \u043d\u0430 \u043c\u043e\u0439 \u0440\u0435\u0433\u0438\u043e\u043d \u043e\u0431\u0438\u0442\u0430\u043d\u0438\u044f. \u041f\u0435\u0440\u0432\u044b\u0439 \u0437\u0430\u043f\u0443\u0441\u043a unreal, \u0438 \u0441\u0440\u0430\u0437\u0443 \u043d\u0430\u0432\u0435\u044f\u043b\u043e \u043f\u0440\u0438\u044f\u0442\u043d\u044b\u0435 \u0432\u043e\u0441\u043f\u043e\u043c\u0438\u043d\u0430\u043d\u0438\u044f \u043e \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435 blender \u0434\u043e 2.8 \u0432\u0435\u0440\u0441\u0438\u0438, \u0438 \u0432 \u0433\u043e\u043b\u043e\u0432\u0435: \u043d\u0443 &#171;\u0433\u043e\u0439\u0434\u0430&#187;, \u0441\u043d\u043e\u0432\u0430 \u0432 \u043d\u0435\u0438\u0437\u0432\u0435\u0434\u0430\u043d\u043d\u044b\u0435 \u0434\u0430\u043b\u0438 <s>\u0441\u0442\u0440\u0430\u0434\u0430\u043d\u0438\u0439<\/s> \u0437\u043d\u0430\u043d\u0438\u0439. \u041e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0432\u0448\u0438\u0441\u044c \u0441 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u043e\u043c, \u043f\u0435\u0440\u0435\u0448\u0451\u043b \u043a \u043a\u0440\u0430\u0442\u043a\u043e\u043c\u0443 \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u043b\u0435\u043d\u0438\u044e \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438 C++. \u041d\u0430 \u0432\u0432\u043e\u0434\u043d\u0443\u044e \u0447\u0430\u0441\u0442\u044c \u0443\u0448\u043b\u043e \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0434\u043d\u0435\u0439, \u0432\u043e\u0437\u0440\u0430\u0441\u0442 \u0434\u0430\u0451\u0442 \u043e \u0441\u0435\u0431\u0435 \u0437\u043d\u0430\u0442\u044c, \u043c\u043e\u0437\u0433 \u043d\u0435 \u0442\u0430\u043a \u0433\u0438\u0431\u043e\u043a \u0438 \u0432\u0441\u044f\u0447\u0435\u0441\u043a\u0438 \u043f\u0440\u043e\u0442\u0438\u0432\u0438\u0442\u044c\u0441\u044f \u0432\u043e\u0441\u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0442\u044c \u043d\u043e\u0432\u0443\u044e \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e, \u0442\u0430\u043a \u0438 \u043d\u043e\u0440\u043e\u0432\u0438\u0442 \u0432\u044b\u0439\u0442\u0438 \u0438\u0437 \u043f\u043e\u0434 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044f \u0438 \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f \u043d\u0430 \u0447\u0442\u0435\u043d\u0438\u0435 \u0447\u0443\u0436\u0438\u0445 \u0441\u0442\u0430\u0442\u0435\u0439 <em>habr<\/em> \u0438 \u043d\u043e\u0432\u043e\u0441\u0442\u0435\u0439 \u0432 <em>3dnews<\/em>. \u041f\u0440\u043e\u0439\u0434\u044f \u043a\u0440\u0430\u0442\u043a\u0438\u0439 \u043a\u0443\u0440\u0441 \u043c\u043e\u043b\u043e\u0434\u043e\u0433\u043e \u0431\u043e\u0439\u0446\u0430, \u043f\u0440\u0438\u0441\u0442\u0443\u043f\u0430\u044e \u043a \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044e \u043f\u0435\u0440\u0432\u043e\u0439 \u0437\u0430\u0434\u0430\u0447\u0438.<\/p>\n<h2>1. \u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0444\u0430\u0439\u043b \u0444\u043e\u0442\u043e\u0433\u0440\u0430\u043c\u043c\u0435\u0442\u0440\u0438\u0438 \u043c\u0435\u0441\u0442\u043d\u043e\u0441\u0442\u0438 \u0432 unreal<\/h2>\n<figure class=\"float full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/603\/b36\/8af\/603b368af87f34deace566bd40a65738.png\" alt=\"\u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0432 Blender\" title=\"\u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0432 Blender\" width=\"3440\" height=\"1440\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/603\/b36\/8af\/603b368af87f34deace566bd40a65738.png\"\/><\/p>\n<div><figcaption>\u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0432 Blender<\/figcaption><\/div>\n<\/figure>\n<p>\u0424\u043e\u0442\u043e\u0433\u0440\u0430\u043c\u043c\u0435\u0442\u0440\u0438\u044f \u043c\u043d\u0435 \u0432\u0441\u0442\u0440\u0435\u0447\u0430\u043b\u0430\u0441\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u0432 \u0441\u0442\u0430\u0442\u044c\u044f\u0445, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043d\u0438\u043a\u0430\u043a\u043e\u0433\u043e \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0441 \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u043c\u0438 \u0434\u0430\u043d\u043d\u044b\u043c\u0438 \u043d\u0435 \u0431\u044b\u043b\u043e, \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0439 \u0444\u0430\u0439\u043b \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0439 \u043e\u0442 \u043d\u043e\u0432\u044b\u0445 \u0442\u043e\u0432\u0430\u0440\u0438\u0449\u0435\u0439-\u0435\u0434\u0438\u043d\u043e\u043c\u044b\u0448\u043b\u0435\u043d\u043d\u0438\u043a\u043e\u0432 \u0431\u044b\u043b \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 \u043d\u0435\u043f\u043e\u043d\u044f\u0442\u043d\u043e\u043c unreal, <strong>.dae<\/strong>. \u0411\u0443\u0434\u0443\u0447\u0438 \u0430\u0432\u0442\u043e\u043d\u043e\u043c\u043d\u043e\u0439 \u0432\u044b\u0447\u0438\u0441\u043b\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u043e\u0439, \u0440\u0435\u0448\u0438\u043b \u0438\u0441\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0432\u043e\u0437\u043d\u0438\u043a\u0448\u0443\u044e \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443 \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e \u043f\u0435\u0440\u0435\u043a\u043e\u0434\u0438\u0440\u043e\u0432\u0430\u0432 \u0432 \u043d\u0443\u0436\u043d\u044b\u0439 \u0444\u043e\u0440\u043c\u0430\u0442. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0441\u044f \u0443\u0436\u0435 \u0434\u043e\u0440\u043e\u0433\u0438\u043c \u043c\u043d\u0435, \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u043d\u044b\u043c \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0435\u043d\u0438\u0435\u043c blender. Blender \u043e\u0442\u043a\u0440\u044b\u043b \u0444\u0430\u0439\u043b \u0431\u0435\u0437 \u043f\u0440\u043e\u0431\u043b\u0435\u043c, \u043a\u043e\u043d\u0432\u0435\u0440\u0442\u0438\u0440\u0443\u044e \u0434\u0430\u043d\u043d\u044b\u0435 \u0432 \u0444\u043e\u0440\u043c\u0430\u0442 .<strong>fbx<\/strong> \u0438 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u044e \u0432 unreal, \u043d\u0430\u0431\u043b\u044e\u0434\u0430\u044e \u043f\u043e\u0434\u043e\u0431\u043d\u0443\u044e \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443:<\/p>\n<figure class=\"bordered full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/597\/cd7\/b98\/597cd7b98cca7de457e2120063e4a73a.png\" alt=\"\u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 - \u043f\u043e\u0442\u0435\u0440\u044f\u043d\u043d\u044b\u0435 \u0433\u0440\u0430\u043d\u0438\" title=\"\u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 - \u043f\u043e\u0442\u0435\u0440\u044f\u043d\u043d\u044b\u0435 \u0433\u0440\u0430\u043d\u0438\" width=\"3373\" height=\"1419\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/597\/cd7\/b98\/597cd7b98cca7de457e2120063e4a73a.png\"\/><\/p>\n<div><figcaption>\u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 &#8212; \u043f\u043e\u0442\u0435\u0440\u044f\u043d\u043d\u044b\u0435 \u0433\u0440\u0430\u043d\u0438<\/figcaption><\/div>\n<\/figure>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/56d\/ce2\/ec3\/56dce2ec3c9a7e61c5ed9e671fda1ffc.png\" alt=\"\u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 - \u043f\u043e\u0442\u0435\u0440\u044f\u043d\u043d\u044b\u0435 \u0433\u0440\u0430\u043d\u0438\" title=\"\u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 - \u043f\u043e\u0442\u0435\u0440\u044f\u043d\u043d\u044b\u0435 \u0433\u0440\u0430\u043d\u0438\" width=\"3440\" height=\"1440\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/56d\/ce2\/ec3\/56dce2ec3c9a7e61c5ed9e671fda1ffc.png\"\/><\/p>\n<div><figcaption>\u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 &#8212; \u043f\u043e\u0442\u0435\u0440\u044f\u043d\u043d\u044b\u0435 \u0433\u0440\u0430\u043d\u0438<\/figcaption><\/div>\n<\/figure>\n<p>\u041d\u0435 \u044f\u0432\u043b\u044f\u044e\u0441\u044c \u044d\u043a\u0441\u043f\u0435\u0440\u0442\u043e\u043c \u0432 3d, \u043b\u044e\u0431\u0438\u0442\u0435\u043b\u044c, \u044d\u0442\u043e\u0442 \u0434\u0435\u0444\u0435\u043a\u0442 \u043c\u043d\u0435 \u0431\u044b\u043b \u043d\u0435 \u043f\u043e\u043d\u044f\u0442\u0435\u043d, \u0431\u043b\u0430\u0433\u043e \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442 \u0432\u0441\u0435\u0433\u0434\u0430 \u0440\u044f\u0434\u043e\u043c. \u0412\u043e\u043e\u0431\u0449\u0435 \u0442\u0440\u0443\u0434\u043d\u043e \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u043e\u0431\u0443\u0447\u0435\u043d\u0438\u0435 \u0442\u0430\u043a\u0438\u043c \u0432\u0435\u0449\u0430\u043c \u0431\u0435\u0437 \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442\u0430, \u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0431\u044b \u043f\u043e\u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043b\u043e\u0441\u044c \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0438 \u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043d\u0443\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0431\u044b \u043f\u043e\u0441\u0435\u0442\u0438\u0442\u044c \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a, \u0447\u0442\u043e \u0431\u044b \u0440\u0435\u0448\u0438\u0442\u044c \u043f\u043e\u0434\u043e\u0431\u043d\u0443\u044e \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443. \u0421\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439 \u0440\u0435\u0446\u0435\u043f\u0442 \u0440\u0435\u0448\u0435\u043d\u0438\u044f \u0438\u0437 \u043f\u0435\u0440\u0432\u044b\u0445 \u0434\u0432\u0443\u0445 \u0441\u0442\u0440\u0430\u043d\u0438\u0446 \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u0438\u043a\u0430, \u043d\u0435 \u043f\u0440\u0438\u043d\u0435\u0441 \u0436\u0435\u043b\u0430\u0435\u043c\u043e\u0433\u043e \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442, \u043d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e\u0435 \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0433\u0440\u0430\u043d\u0435\u0439. \u0421\u043a\u0443\u0434\u043d\u043e\u0441\u0442\u044c \u043c\u043e\u0435\u0433\u043e \u043a\u0443\u043b\u044c\u0442\u0443\u0440\u043d\u043e\u0433\u043e \u0441\u043b\u043e\u0432\u0430\u0440\u043d\u043e\u0433\u043e \u0437\u0430\u043f\u0430\u0441\u0430, \u043d\u0435 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043b\u0438\u0442\u0435\u0440\u0430\u0442\u0443\u0440\u043d\u043e \u043e\u043f\u0438\u0441\u0430\u0442\u044c \u043d\u0435\u0434\u043e\u0432\u043e\u043b\u044c\u0441\u0442\u0432\u043e. \u042f \u043d\u0430\u043f\u043e\u043b\u043d\u0438\u043b\u0441\u044f \u043d\u0435\u0433\u043e\u0434\u043e\u0432\u0430\u043d\u0438\u0435\u043c, \u043d\u043e \u0432\u0441\u0451 \u0436\u0435 \u0442\u0435\u0440\u043f\u0435\u043d\u0438\u0435 \u0438 \u0441\u043e\u0441\u0440\u0435\u0434\u043e\u0442\u043e\u0447\u0435\u043d\u043d\u043e\u0441\u0442\u044c \u043f\u0440\u0438\u0432\u0435\u043b\u0438 \u043c\u0435\u043d\u044f \u043a &#171;\u0434\u0435\u043b\u0430\u0435\u043c\u043e\u043c\u0443&#187;. \u0417\u0430 \u044d\u0442\u0438\u043c\u0438 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u043c\u0438 \u0441\u0442\u0440\u043e\u043a\u0430\u043c\u0438 \u0441\u043a\u0440\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043e\u0447\u0435\u043d\u044c \u043c\u043d\u043e\u0433\u043e \u043f\u0435\u0440\u0435\u0431\u043e\u0440\u0430, \u0445\u043e\u0440\u043e\u0448\u043e \u0447\u0442\u043e \u0434\u043e\u0434\u0443\u043c\u0430\u043b\u0441\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c, &#171;\u0431\u043e\u043b\u044c\u0448\u043e\u0439&#187; \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u0444\u0430\u0439\u043b \u044d\u0442\u043e \u0434\u043e\u043b\u0433\u0430\u044f \u043f\u0440\u043e\u0446\u0435\u0434\u0443\u0440\u0430, \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c blender&#8230; <\/p>\n<details class=\"spoiler\">\n<summary>\u041a\u043e\u0434, \u0440\u0435\u0448\u0438\u0432\u0448\u0438\u0439 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">import bpy, bmesh from mathutils import Vector, Matrix import numpy as np   # \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043e\u0431\u044c\u0435\u043a\u0442 \u0438\u0437 \u0441\u0446\u0435\u043d\u044b bpy.context.active_object.select_set(False) foto_obj = bpy.context.scene.objects[0] bpy.context.view_layer.objects.active = foto_obj foto_obj.select_set(True)  # \u0412\u044b\u0431\u0440\u0430\u0442\u044c \u0432\u0441\u0435 \u0433\u0440\u0430\u043d\u0438 me = foto_obj.data bpy.ops.object.mode_set(mode=\"EDIT\") bpy.ops.mesh.select_mode(type = 'EDGE') bpy.ops.mesh.select_all(action = 'SELECT') \"\"\" \u0424\u0443\u043d\u043a\u0446\u0438\u044f Beautify Faces \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e  \u0441 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u043c\u0438 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u043c\u0438 \u0433\u0440\u0430\u043d\u044f\u043c\u0438.  \u041f\u0435\u0440\u0435\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0435 \u0442\u0440\u0435\u0443\u0433\u043e\u043b\u044c\u043d\u0438\u043a\u0438,  \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0431\u043e\u043b\u0435\u0435 \u00ab\u0441\u0431\u0430\u043b\u0430\u043d\u0441\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435\u00bb  (\u0442.\u0435. \u043c\u0435\u043d\u0435\u0435 \u0434\u043b\u0438\u043d\u043d\u044b\u0435 \u0438 \u0442\u043e\u043d\u043a\u0438\u0435 \u0442\u0440\u0435\u0443\u0433\u043e\u043b\u044c\u043d\u0438\u043a\u0438). \"\"\" bpy.ops.mesh.beautify_fill() bpy.ops.mesh.normals_tools(mode=\"RESET\")<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u0418\u0442\u043e\u0433\u043e\u0432\u044b\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442:<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/c72\/a82\/c20\/c72a82c20a0f6144c2c4bdb4d676e688.png\" alt=\"\u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0441 \u0438\u0441\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u043c\u0438 \u0433\u0440\u0430\u043d\u044f\u043c\u0438\" title=\"\u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0441 \u0438\u0441\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u043c\u0438 \u0433\u0440\u0430\u043d\u044f\u043c\u0438\" width=\"2146\" height=\"929\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/c72\/a82\/c20\/c72a82c20a0f6144c2c4bdb4d676e688.png\"\/><\/p>\n<div><figcaption>\u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0441 \u0438\u0441\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u043c\u0438 \u0433\u0440\u0430\u043d\u044f\u043c\u0438<\/figcaption><\/div>\n<\/figure>\n<p>\u041d\u043e \u043a\u0430\u043a \u0432\u0441\u0435\u0433\u0434\u0430 \u0432 \u043d\u0430\u0448\u0435\u043c \u0434\u0435\u043b\u0435, \u043e\u0434\u043d\u0430 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u0437\u0430 \u0434\u0440\u0443\u0433\u043e\u0439, \u0432 \u043c\u043e\u0451\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u044d\u0442\u0438 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u0441\u0432\u044f\u0437\u0430\u043d\u044b \u0441 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u0435\u043c \u0432 \u0442\u043e\u0442 \u043c\u043e\u043c\u0435\u043d\u0442 \u0437\u043d\u0430\u043d\u0438\u0439 \u043d\u0443\u0436\u043d\u044b\u0445 \u0434\u043b\u044f \u043f\u043e\u043d\u0438\u043c\u0430\u043d\u0438\u044f \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u044f\u0449\u0435\u0433\u043e. \u0424\u0430\u0439\u043b \u0444\u043e\u0442\u043e\u0433\u0440\u0430\u043c\u043c\u0435\u0442\u0440\u0438\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u044b\u0439 \u0432 agisoft \u0437\u0430\u0448\u0438\u0444\u0440\u043e\u0432\u044b\u0432\u0430\u0435\u0442 \u043a\u0443\u0441\u043e\u0447\u043a\u0438 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438 \u0432 \u0433\u0440\u0430\u043d\u0438, \u044d\u0442\u043e \u0441\u043e\u0437\u0434\u0430\u0451\u0442 \u044d\u0444\u0444\u0435\u043a\u0442 \u043e\u0431\u044a\u0451\u043c\u0430. \u0412 blender \u043c\u043e\u0436\u043d\u043e \u043b\u0435\u0433\u043a\u043e \u0432\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0438 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0442\u044c \u0431\u0435\u0437 \u043f\u0440\u043e\u0431\u043b\u0435\u043c, \u043d\u043e \u0432 unreal \u043d\u0435 \u0441\u043c\u043e\u0433 \u043d\u0430\u0439\u0442\u0438 \u0441\u043f\u043e\u0441\u043e\u0431 \u043e\u0442\u043e\u0431\u0440\u0430\u0437\u0438\u0442\u044c \u0442\u0430\u043a \u0436\u0435 \u043a\u0430\u043a \u0432 blender, \u0435\u0441\u043b\u0438 \u0441\u0442\u0430\u043b\u043a\u0438\u0432\u0430\u043b\u0438\u0441\u044c \u0441 \u043f\u043e\u0434\u043e\u0431\u043d\u044b\u043c, \u043f\u043e\u0434\u0435\u043b\u0438\u0442\u0435\u0441\u044c \u043e\u043f\u044b\u0442\u043e\u043c. \u0418 \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0433\u043e \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0443 \u043c\u0435\u043d\u044f \u043d\u0435\u0442, \u0434\u043b\u044f \u043d\u0430\u043b\u043e\u0436\u0435\u043d\u0438\u0438 \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0442\u0435\u043a\u0441\u0442\u0443\u0440\u044b. \u041d\u0435 \u0438\u0437\u043c\u0435\u043d\u044f\u044f \u0441\u0432\u043e\u0438\u043c \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u0430\u043c, \u0441 \u044d\u0442\u0438\u043c, \u043a\u0430\u0437\u0443\u0441\u043e\u043c, \u0431\u0443\u0434\u0443 \u0440\u0430\u0437\u0431\u0438\u0440\u0430\u0442\u044c\u0441\u044f \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e. \u0421\u043e\u0437\u0434\u0430\u043b \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435, \u0441\u043f\u043e\u0441\u043e\u0431\u043e\u043c \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0430\u0437\u044b\u0432\u0430\u044e\u0442 \u0437\u0430\u043f\u0435\u043a\u0430\u043d\u0438\u0435 \u0442\u0435\u043a\u0441\u0442\u0443\u0440\u044b. \u041a\u043e\u0433\u0434\u0430 \u0431\u0443\u0434\u0438\u0442\u0435 \u0438\u0441\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u043c\u0435\u043d\u044f \u0432 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u044f\u0445, \u0443\u0447\u0438\u0442\u044b\u0432\u0430\u0439\u0442\u0435 \u0447\u0442\u043e \u044f \u043d\u0435 \u044d\u043a\u0441\u043f\u0435\u0440\u0442 \u0432 \u044d\u0442\u043e\u0439 \u043e\u0431\u043b\u0430\u0441\u0442\u0438, \u043b\u044e\u0431\u0438\u0442\u0435\u043b\u044c, \u0431\u0443\u0434\u044c\u0442\u0435 \u0442\u0430\u043a\u0442\u0438\u0447\u043d\u0435\u0439. \u0412\u0438\u0434\u0435\u043e \u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f:<\/p>\n<div class=\"tm-iframe_temp\" data-src=\"https:\/\/embedd.srv.habr.com\/iframe\/6509ffe26f566b30a56669b5\" data-style=\"\" id=\"6509ffe26f566b30a56669b5\" width=\"\"><\/div>\n<p>\u041f\u043e\u043b\u0443\u0447\u0438\u0432 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0432 \u043f\u043e\u0434\u0445\u043e\u0434\u044f\u0449\u0435\u043c \u0434\u043b\u044f \u043c\u0435\u043d\u044f \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u0438, \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u044e\u0441\u044c \u0432 unreal \u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u044e \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u0443\u044e \u0442\u0435\u043a\u0441\u0442\u0443\u0440\u0443. \u0415\u0441\u0442\u044c \u043d\u044e\u0430\u043d\u0441\u044b \u043e \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u044f \u043d\u0435 \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u043b\/\u043f\u0438\u0441\u0430\u043b, \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e \u0443\u0437\u043d\u0430\u0442\u044c \u0438\u0437 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0435\u0432 \u043e \u0447\u0435\u043c \u0443\u043c\u043e\u043b\u0447\u0430\u043b. \u0418\u0442\u043e\u0433\u043e\u0432\u044b\u0439 \u0432\u0430\u0440\u0438\u0430\u043d\u0442:<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/05c\/4a2\/53a\/05c4a253a8f29c0bde9d513f058ed5b4.png\" alt=\"\u0441 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0451\u043d\u043d\u043e\u0439 \u0442\u0435\u043a\u0441\u0442\u0443\u0440\u043e\u0439\" title=\"\u0441 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0451\u043d\u043d\u043e\u0439 \u0442\u0435\u043a\u0441\u0442\u0443\u0440\u043e\u0439\" width=\"3440\" height=\"1440\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/05c\/4a2\/53a\/05c4a253a8f29c0bde9d513f058ed5b4.png\"\/><\/p>\n<div><figcaption>\u0441 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0451\u043d\u043d\u043e\u0439 \u0442\u0435\u043a\u0441\u0442\u0443\u0440\u043e\u0439<\/figcaption><\/div>\n<\/figure>\n<p>\u041f\u0435\u0440\u0432\u044b\u0439 \u043f\u0443\u043d\u043a\u0442 \u043f\u043b\u0430\u043d\u0430 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d, \u0434\u0432\u0438\u0433\u0430\u044e\u0441\u044c \u0434\u0430\u043b\u044c\u0448\u0435. \u0412 \u043d\u0430\u0447\u0430\u043b\u0435 \u0441\u0442\u0430\u0442\u044c\u0438, \u043f\u043e\u0441\u0442\u0430\u0432\u0438\u043b \u0446\u0435\u043b\u044c \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0432\u0435\u0441\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f C++ \u043d\u0435 \u043f\u0440\u0438\u0431\u0435\u0433\u0430\u044f \u043a blueprint. \u0411\u0443\u0434\u0443 \u043f\u0440\u0438\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0442\u044c\u0441\u044f \u044d\u0442\u043e\u0439 \u0446\u0435\u043b\u0438, \u0442\u0430\u043a\u043e\u0439 \u043f\u043e\u0434\u0445\u043e\u0434 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043c\u043d\u0435 \u0445\u043e\u0440\u043e\u0448\u043e \u043f\u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u044c\u0441\u044f \u0441 \u043a\u043e\u043d\u0446\u0435\u043f\u0446\u0438\u0435\u0439 unreal. \u041f\u043e\u043b\u0443\u0447\u0430\u0435\u043c\u044b\u0439 \u043e\u043f\u044b\u0442 \u0431\u0443\u0434\u0443 \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0442\u044c \u0434\u043b\u044f \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0433\u043e \u044d\u0442\u0430\u043f\u0430 \u0440\u0430\u0431\u043e\u0442\u044b. \u0415\u0441\u0442\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0432 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0435 \u0437\u043d\u0430\u043a\u043e\u043c\u0441\u0442\u0432\u0430\/\u043f\u043e\u0438\u0441\u043a\u0430 \u0432 \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442\u0435 \u043e\u0442\u0432\u0435\u0442\u043e\u0432 \u043d\u0430 \u0442\u0440\u0443\u0434\u043d\u043e\u0441\u0442\u0438, \u043f\u0440\u0438\u0448\u043b\u043e\u0441\u044c \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043e\u0441\u0432\u043e\u0438\u0442\u044c bluerprint, \u0432\u0441\u044f \u0438\u0434\u0435\u043e\u043b\u043e\u0433\u0438\u044f unreal \u043f\u043e \u0431\u043e\u043b\u044c\u0448\u0435\u0439 \u0447\u0430\u0441\u0442\u0438 \u043f\u043e\u0441\u0442\u0440\u043e\u0435\u043d\u0430 \u043d\u0430 \u0442\u0435\u0441\u043d\u043e\u043c \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0438 \u043c\u0435\u0436\u0434\u0443 \u0442\u0435\u043a\u0441\u0442\u043e\u0432\u044b\u043c \u0438 \u0432\u0438\u0437\u0443\u0430\u043b\u044c\u043d\u044b\u043c \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435\u043c, \u0442\u0430\u043a\u043e\u0439 \u0441\u0435\u0431\u0435 \u0441\u0438\u043c\u0431\u0438\u043e\u0437. \u0412 \u0442\u043e \u0436\u0435 \u0432\u0440\u0435\u043c\u044f \u043d\u0435\u0442 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0439, \u0432 \u043f\u0440\u0430\u0432\u0435 \u0432\u044b\u0431\u0440\u0430\u0442\u044c \u043e\u0434\u0438\u043d \u0441\u043f\u043e\u0441\u043e\u0431 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u0434\u0445\u043e\u0434\u0438\u0442 \u0438\u043c\u0435\u043d\u043d\u043e \u043c\u043d\u0435, \u0434\u043b\u044f \u0441\u0432\u043e\u0438\u0445 \u0442\u0432\u043e\u0440\u0447\u0435\u0441\u043a\u0438\u0445 \u0438\u0437\u044b\u0441\u043a\u0430\u043d\u0438\u0439, \u0438 \u0432\u0441\u0451 \u0436\u0435 \u043d\u0443\u0436\u043d\u043e \u043f\u0440\u043e\u043d\u0438\u043a\u043d\u0443\u0442\u044c\u0441\u044f \u0438\u0434\u0435\u043e\u043b\u043e\u0433\u0438\u0435\u0439 \u0437\u0430\u043b\u043e\u0436\u0435\u043d\u043d\u043e\u0439 \u0441\u043e\u0437\u0434\u0430\u0442\u0435\u043b\u044f\u043c\u0438 unrel \u0447\u0442\u043e \u0431\u044b \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e \u043f\u043e\u043d\u0438\u043c\u0430\u0442\u044c \u043a\u0430\u043a\u043e\u0439 \u043f\u043e\u0434\u0445\u043e\u0434 \u043b\u0443\u0447\u0448\u0435 \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0442\u044c \u0432 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0445 \u0443\u0441\u043b\u043e\u0432\u0438\u044f\u0445, \u043e\u0447\u0435\u043d\u044c \u0441\u0438\u043b\u044c\u043d\u043e \u0432\u043b\u0438\u044f\u0435\u0442 \u043d\u0430 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c, \u043e \u0447\u0451\u043c \u0440\u0435\u0433\u0443\u043b\u044f\u0440\u043d\u043e \u0441\u043e\u043e\u0431\u0449\u0430\u044e\u0442 \u0432 \u0441\u0432\u043e\u0435\u0439 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0438 \u0438\u0437 epic. \u0422\u0432\u043e\u0440\u0447\u0435\u0441\u0442\u0432\u043e \u0432 unreal \u0441\u0438\u043b\u0430\u043c\u0438 C++ \u0431\u043e\u043b\u0435\u0435 \u0433\u0438\u0431\u043a\u043e\u0435 \u0438 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0435 \u0432 \u0433\u043e\u0442\u043e\u0432\u043e\u043c \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u0435, \u043d\u043e \u043a\u0430\u043a \u0438 \u0432\u0441\u0435 \u0441\u0432\u044f\u0437\u0430\u043d\u043d\u043e\u0435 \u0441 C++, \u043c\u043d\u043e\u0433\u043e, \u043e\u0447\u0435\u043d\u044c \u043c\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430, \u044d\u0442\u043e \u043d\u0435 python. \u041f\u0440\u0438\u0441\u0442\u0443\u043f\u0430\u044e \u043a \u0440\u0435\u0448\u0435\u043d\u0438\u044e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0439 \u0437\u0430\u0434\u0430\u0447\u0438, \u043f\u043e\u0441\u043b\u0435 \u0442\u0430\u043a\u043e\u0433\u043e \u043b\u0438\u0440\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u043e\u0442\u0441\u0442\u0443\u043f\u043b\u0435\u043d\u0438\u044f.<\/p>\n<h2>2. \u041d\u0430\u0447\u0435\u0440\u0442\u0438\u0442\u044c \u043f\u0443\u0442\u044c<\/h2>\n<p>\u0414\u043b\u044f \u0442\u0435\u043a\u0443\u0449\u0435\u0439 \u0437\u0430\u0434\u0430\u0447\u0438 \u0431\u0443\u0434\u0443 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c spline, \u043b\u043e\u0433\u0438\u0447\u043d\u043e \u0434\u043b\u044f \u043f\u043e\u0434\u043e\u0431\u043d\u043e\u0439 \u0437\u0430\u0434\u0430\u0447\u0438. \u0414\u043b\u044f \u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u0430 \u0447\u0438\u0442\u0430\u044e\u0449\u0438\u0445 &#171;\u0437\u0430\u043d\u0435\u0441\u0451\u043d\u043d\u044b\u0445&#187; \u0432 \u044d\u0442\u0443 \u0441\u0442\u0430\u0442\u044c\u044e, \u043d\u0435\u0442 \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u043e\u0432\u043e\u0433\u043e, \u0432\u0441\u0442\u0440\u0435\u0447\u0430\u0435\u0442\u0441\u044f \u0432\u043e \u0432\u0441\u0435\u0445 \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u044b\u0445 3d \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0430\u0445, \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u0443\u043f\u0440\u043e\u0449\u0430\u044e\u0449\u0438\u0439 \u0442\u0435\u0445\u043d\u043e \u0438\u0437\u0432\u0440\u0430\u0449\u0435\u043d\u0438\u044f. \u0421\u043e\u0437\u0434\u0430\u044e C++ \u043a\u043b\u0430\u0441\u0441 \u0430\u043a\u0442\u0435\u0440 \u0441 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435\u043c <em>FlightActor<\/em>.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/b48\/7b4\/d10\/b487b4d1023d00f58db7514120aec8bf.png\" alt=\"\u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 c++ \u043a\u043b\u0430\u0441\u0441\u0430 FlightActor\" title=\"\u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 c++ \u043a\u043b\u0430\u0441\u0441\u0430 FlightActor\" width=\"1920\" height=\"1080\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/b48\/7b4\/d10\/b487b4d1023d00f58db7514120aec8bf.png\"\/><\/p>\n<div><figcaption>\u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 c++ \u043a\u043b\u0430\u0441\u0441\u0430 FlightActor<\/figcaption><\/div>\n<\/figure>\n<p>\u0417\u0430\u043f\u043e\u043b\u043d\u044f\u044e \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0435 \u0444\u0430\u0439\u043b\u044b \u043a\u043e\u0434\u043e\u043c:<\/p>\n<details class=\"spoiler\">\n<summary>FlightActor.h<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"cpp\">\/\/ Fill out your copyright notice in the Description page of Project Settings.  #pragma once  #include \"CoreMinimal.h\" #include \"GameFramework\/Actor.h\" #include \"aboveActor.generated.h\"  UCLASS() class ABOVE2_API AaboveActor : public AActor { GENERATED_BODY()  public: \/\/ Sets default values for this actor's properties     AaboveActor();     class USplineComponent* MySpline;     class APlayerController* MyPlayerController;  protected: \/\/ Called when the game starts or when spawned virtual void BeginPlay() override;  public: \/\/ Called every frame     virtual void Tick(float DeltaTime) override;     virtual void PostActorCreated() override;     void SetPosition(); }; <\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>FlightActor.cpp<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"cpp\">\/\/ Fill out your copyright notice in the Description page of Project Settings. #include \"aboveActor.h\" #include \"Logging\/LogMacros.h\" #include \"Components\/SplineComponent.h\" #include \"Kismet\/GameplayStatics.h\"  #include \"Components\/SplineMeshComponent.h\" #include \"Components\/SceneComponent.h\" #include \"Engine\/StaticMesh.h\" #include \"Containers\/Array.h\"   \/\/ Sets default values AaboveActor::AaboveActor() {  \/\/ Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it. PrimaryActorTick.bCanEverTick = true;     \/\/ Add spline to actor     this->MySpline = CreateDefaultSubobject&lt;USplineComponent>(\"MySpline\",true);     SetRootComponent(this->MySpline);     this->MySpline->bDrawDebug = true;     this->MyPlayerController = UGameplayStatics::GetPlayerController(this, 0);     int32 NumberPoints = MySpline->GetNumberOfSplinePoints();     UE_LOG(LogTemp, Warning, TEXT(\"Test Log. Num Points %d\"), NumberPoints); \/\/Display }  \/\/ Called when the game starts or when spawned void AaboveActor::BeginPlay() { Super::BeginPlay();     float LenGth_s = this->MySpline->GetSplineLength();     FVector actLocation = this->GetActorLocation();     UE_LOG(LogTemp, Warning, TEXT(\"SplineLength-->%f, Location-->%s\"), LenGth_s, *actLocation.ToString()); }  \/\/ Called every frame void AaboveActor::Tick(float DeltaTime) { Super::Tick(DeltaTime); }  \/\/ This is called when actor is spawned (at runtime or when you drop it into the world in editor) void AaboveActor::PostActorCreated() {     Super::PostActorCreated();     SetPosition(); }  void AaboveActor::SetPosition() {     FVector startPos = FVector(0.0f,0.0f,20.0f);     this->SetActorLocation(startPos);     UE_LOG(LogTemp, Warning, TEXT(\"START THIS !!!\")); }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u041a\u043e\u043c\u043f\u0438\u043b\u0438\u0440\u0443\u044e, \u0440\u0430\u0434\u0443\u044e\u0441\u044c \u043f\u0435\u0440\u0432\u043e\u043c\u0443 \u0440\u0430\u0431\u043e\u0447\u0435\u043c\u0443 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0443 \u0432 unreal. \u041f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043b <em>PostActorCreated() <\/em>\u0434\u043b\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u043f\u043e\u0437\u0438\u0446\u0438\u043e\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u0432 \u043d\u0443\u0436\u043d\u044b\u0435 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b \u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u043b spline \u043a \u0430\u043a\u0442\u0451\u0440\u0443. \u041a \u0441\u0447\u0430\u0441\u0442\u044c\u044e \u043c\u043e\u0451 \u0440\u0443\u043a\u043e\u0432\u043e\u0434\u0441\u0442\u0432\u043e \u043d\u0435 \u043f\u0440\u043e \u0442\u0435\u043e\u0440\u0438\u044e, \u0430 \u043f\u0440\u043e \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043d\u043e\u0439 \u0437\u0430\u0434\u0430\u0447\u0438, \u0438 \u044f \u0432 \u043f\u0440\u0430\u0432\u0435 \u043d\u0435 \u043f\u0438\u0441\u0430\u0442\u044c \u043e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u043d\u044b\u0445 \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0441\u0442\u044f\u0445 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432, \u043f\u043e\u0447\u0435\u043c\u0443 \u0442\u0430\u043a \u0430 \u043d\u0435 \u0438\u043d\u0430\u0447\u0435 \u043b\u0443\u0447\u0448\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043e\u0442\u0432\u0435\u0442 \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e. \u0412\u0441\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442, \u0442\u0435\u043f\u0435\u0440\u044c \u043d\u0443\u0436\u043d\u043e \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c \u043a\u043e\u0434 \u043d\u0435\u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0432 \u043c\u043e\u0435\u0439 \u0437\u0430\u0434\u0430\u0447\u0435. \u041a\u043e\u0434 \u043f\u043e\u043a\u0430\u0436\u0435\u0442\u0441\u044f \u0437\u043d\u0430\u043a\u043e\u043c\u044b\u043c, \u0438 \u044d\u0442\u043e \u043d\u0435 \u0443\u0434\u0438\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e, \u0443\u0447\u0435\u0431\u043d\u044b\u0435 \u043f\u043e\u0441\u043e\u0431\u0438\u044f \u0434\u043b\u044f \u0432\u0441\u0435\u0445 \u043e\u0434\u0438\u043d\u0430\u043a\u043e\u0432\u044b&#8230;<\/p>\n<details class=\"spoiler\">\n<summary>FlightActor.h<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"cpp\">\/\/ Fill out your copyright notice in the Description page of Project Settings.  #pragma once  #include \"CoreMinimal.h\" #include \"GameFramework\/Actor.h\" #include \"Components\/SplineComponent.h\" #include \"DrawDebugHelpers.h\" #include \"FlightActor.generated.h\"  UCLASS() class ABOVE2_API AFlightActor : public AActor { GENERATED_BODY()  public: \/\/ Sets default values for this actor's properties AFlightActor();      \/** Returns the next flight curve *\/     UCurveFloat* GetFlightCurve() { return FlightCurve; };      \/** Returns the next flight spline component *\/     USplineComponent* GetFlightSplineComp() { return FlightComp; };  protected:     \/** The FloatCurve corresponding to the next flight spline component *\/     UPROPERTY(EditAnywhere)     UCurveFloat* FlightCurve;      \/** A static mesh for our flight stop *\/     UPROPERTY(VisibleAnywhere)     UStaticMeshComponent* SM;      \/** The spline component that describes the flight path of the next flight *\/     UPROPERTY(VisibleAnywhere)     USplineComponent* FlightComp; }; <\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>FlightActor.cpp<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"cpp\">\/\/ Fill out your copyright notice in the Description page of Project Settings. #include \"FlightActor.h\" #include \"Logging\/LogMacros.h\"  \/\/ Sets default values AFlightActor::AFlightActor() {  \/\/ Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it. PrimaryActorTick.bCanEverTick = true;      SM = CreateDefaultSubobject&lt;UStaticMeshComponent>(FName(\"SM\"));     SetRootComponent(SM);      \/\/Init splines     FlightComp = CreateDefaultSubobject&lt;USplineComponent>(FName(\"SplineComp\"));      \/\/Attach them to root component     FlightComp->SetupAttachment(SM); }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<h2>3. \u041f\u043e \u0437\u0430\u0434\u0430\u043d\u043d\u043e\u043c\u0443 \u043f\u0443\u0442\u0438 \u0434\u0432\u0438\u0433\u0430\u0442\u044c \u043a\u0430\u043c\u0435\u0440\u0443<\/h2>\n<p>\u0412 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u043c \u044d\u0442\u0430\u043f\u0435 \u0441\u043e\u0437\u0434\u0430\u043b \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u043d\u043e spline \u0434\u043b\u044f \u043f\u043e\u0441\u0442\u0440\u043e\u0435\u043d\u0438\u044f \u0442\u0440\u0430\u0435\u043a\u0442\u043e\u0440\u0438\u0438 \u0436\u0435\u043b\u0430\u0435\u043c\u043e\u0433\u043e \u043c\u0430\u0440\u0448\u0440\u0443\u0442\u0430, \u043a \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u043e\u043c\u0443 \u043d\u0443\u0436\u043d\u043e \u043f\u0440\u0438\u043a\u0440\u0435\u043f\u0438\u0442\u044c \u043e\u0431\u044a\u0435\u043a\u0442 \u0434\u043b\u044f \u0434\u0432\u0438\u0436\u0435\u043d\u0438\u044f \u043f\u043e \u0442\u0440\u0430\u0435\u043a\u0442\u043e\u0440\u0438\u0438. \u0412 \u044d\u0442\u043e\u0442 \u0440\u0430\u0437 \u0441\u043e\u0437\u0434\u0430\u044e \u043a\u043b\u0430\u0441\u0441 character, \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0435 \u0444\u0430\u0439\u043b\u044b \u043f\u0440\u0438\u0432\u043e\u0436\u0443 \u043a \u0442\u0430\u043a\u043e\u043c\u0443 \u0432\u0438\u0434\u0443:<\/p>\n<details class=\"spoiler\">\n<summary>FlightC.h<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"cpp\">\/\/ Fill out your copyright notice in the Description page of Project Settings.  #pragma once  #include \"CoreMinimal.h\" #include \"Components\/TimelineComponent.h\" #include \"Components\/BoxComponent.h\" #include \"GameFramework\/Character.h\" #include \"FlightActor.h\" #include \"CaptureManager.h\" \/\/ \u0435\u0449\u0435 \u043d\u0435 \u0441\u043e\u0437\u0434\u0430\u043d\u043e #include \"FlightC.generated.h\"  UCLASS(config=Game) class ABOVE2_API AFlightC : public ACharacter { GENERATED_BODY()     UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = \"true\"))    class USpringArmComponent* CameraBoom;     UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = \"true\"))    class UCameraComponent* FollowCamera;  public: \/\/ Sets default values for this character's properties AFlightC();      FTimeline FlightTimeline;      UFUNCTION()     void TickTimeline(float Value);      \/** Base turn rate, in deg\/sec. Other scaling may affect final turn rate. *\/     UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category=Camera)     float BaseTurnRate;      \/** Base look up\/down rate, in deg\/sec. Other scaling may affect final rate. *\/     UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category=Camera)     float BaseLookUpRate;      \/\/ \u0435\u0449\u0435 \u043d\u0435 \u0441\u043e\u0437\u0434\u0430\u043d\u043e     UPROPERTY(EditAnywhere)     class ACaptureManager* CaptureActor;  \/\/Create a pointer to your another class      \/** The active spline component, meaning the flight path that the character is currently following *\/     USplineComponent* ActiveSplineComponent;      \/** The selected flight stop actor *\/     AFlightActor* ActiveFlightActor;     \/** Box overlap function *\/     UFUNCTION()     void OnFlightBoxColliderOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult&amp; SweepResult);      \/** Executes when we're pressing the FlightPath key bind *\/     void FlightPathSelected();      \/** Updates the flight timeline with a new curve and starts the flight *\/     void UpdateFlightTimeline(UCurveFloat* CurveFloatToBind);      UFUNCTION()     void ResetActiveFlightActor();   protected: \/\/ Called when the game starts or when spawned     virtual void BeginPlay() override;      \/*The Box component that detects any nearby flight stops*\/     UPROPERTY(VisibleAnywhere)     UBoxComponent* FlightBoxCollider;      \/** Called for forwards\/backward input *\/     void MoveForward(float Value);      \/** Called for side to side input *\/     void MoveRight(float Value);      \/**      * Called via input to turn at a given rate.      * @param RateThis is a normalized rate, i.e. 1.0 means 100% of desired turn rate      *\/     void TurnAtRate(float Rate);      \/**      * Called via input to turn look up\/down at a given rate.      * @param RateThis is a normalized rate, i.e. 1.0 means 100% of desired turn rate      *\/     void LookUpAtRate(float Rate);      \/** Handler for when a touch input begins. *\/     void TouchStarted(ETouchIndex::Type FingerIndex, FVector Location);      \/** Handler for when a touch input stops. *\/     void TouchStopped(ETouchIndex::Type FingerIndex, FVector Location);   public: \/\/ Called every frame virtual void Tick(float DeltaTime) override;  \/\/ Called to bind functionality to input     virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;      \/** Returns CameraBoom subobject **\/     FORCEINLINE class USpringArmComponent* GetCameraBoom() const { return CameraBoom; }     \/** Returns FollowCamera subobject **\/     FORCEINLINE class UCameraComponent* GetFollowCamera() const { return FollowCamera; } }; <\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>FlightC.cpp<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"cpp\">\/\/ Fill out your copyright notice in the Description page of Project Settings. #include \"FlightC.h\" #include \"GameFramework\/SpringArmComponent.h\" #include \"Camera\/CameraComponent.h\" #include \"Components\/CapsuleComponent.h\" #include \"Components\/SceneCaptureComponent2D.h\" #include \"HeadMountedDisplayFunctionLibrary.h\" #include \"Kismet\/GameplayStatics.h\" #include \"CaptureManager.h\" #include \"Logging\/LogMacros.h\"   \/\/ Sets default values AFlightC::AFlightC() {  \/\/ Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.     PrimaryActorTick.bCanEverTick = true;      \/\/ Set size for collision capsule     GetCapsuleComponent()->InitCapsuleSize(42.f, 96.0f);      \/\/ set our turn rates for input     BaseTurnRate = 45.f;     BaseLookUpRate = 45.f;      \/\/ Don't rotate when the controller rotates. Let that just affect the camera.     bUseControllerRotationPitch = false;     bUseControllerRotationYaw = false;     bUseControllerRotationRoll = false;      \/\/ Create a camera boom (pulls in towards the player if there is a collision)     CameraBoom = CreateDefaultSubobject&lt;USpringArmComponent>(TEXT(\"CameraBoom\"));     CameraBoom->SetupAttachment(RootComponent);     CameraBoom->TargetArmLength = 300.0f; \/\/ The camera follows at this distance behind the character     CameraBoom->bUsePawnControlRotation = true; \/\/ Rotate the arm based on the controller      \/\/ Create a follow camera     FollowCamera = CreateDefaultSubobject&lt;UCameraComponent>(TEXT(\"FollowCamera\"));     FollowCamera->SetupAttachment(CameraBoom, USpringArmComponent::SocketName); \/\/ Attach the camera to the end of the boom and let the boom adjust to match the controller orientation     FollowCamera->bUsePawnControlRotation = false; \/\/ Camera does not rotate relative to arm      \/\/ Note: The skeletal mesh and anim blueprint references on the Mesh component (inherited from Character)     \/\/ are set in the derived blueprint asset named MyCharacter (to avoid direct content references in C++)     FlightBoxCollider = CreateDefaultSubobject&lt;UBoxComponent>(FName(\"FlightBoxCollider\"));     FlightBoxCollider->SetBoxExtent(FVector(150.f));     FlightBoxCollider->SetupAttachment(GetRootComponent());  }   \/\/ Called when the game starts or when spawned void AFlightC::BeginPlay() {     Super::BeginPlay();     \/\/Register a function that gets called when the box overlaps with a component     FlightBoxCollider->OnComponentBeginOverlap.AddDynamic(this, &amp;AFlightC::OnFlightBoxColliderOverlap);      \/\/ \u0435\u0449\u0435 \u043d\u0435 \u0441\u043e\u0437\u0434\u0430\u043d\u043e, \u0441\u043c. 4 \u043f\u0443\u043d\u043a\u0442       \/\/ https:\/\/code911.top\/howto\/unreal-how-to-delete-link-code-example     AActor* FoundActor = UGameplayStatics::GetActorOfClass(GetWorld(), ACaptureManager::StaticClass());     CaptureActor = Cast&lt;ACaptureManager>(FoundActor);     if (CaptureActor)     {         UE_LOG(LogTemp, Warning, TEXT(\"CaptureActor.... %s\"), *CaptureActor->GetFName().ToString());     }  }  \/\/ Called every frame void AFlightC::Tick(float DeltaTime) { Super::Tick(DeltaTime);      \/\/If the timeline has started, advance it by DeltaSeconds     if (FlightTimeline.IsPlaying()) FlightTimeline.TickTimeline(DeltaTime);  }  \/\/ Called to bind functionality to input void AFlightC::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) {     \/\/ Set up gameplay key bindings     check(PlayerInputComponent);     PlayerInputComponent->BindAction(\"Jump\", IE_Pressed, this, &amp;ACharacter::Jump);     PlayerInputComponent->BindAction(\"Jump\", IE_Released, this, &amp;ACharacter::StopJumping);      PlayerInputComponent->BindAxis(\"MoveForward\", this, &amp;AFlightC::MoveForward);     PlayerInputComponent->BindAxis(\"MoveRight\", this, &amp;AFlightC::MoveRight);      \/\/ Bind the functions that execute on key press     PlayerInputComponent->BindAction(\"FlightPath\", IE_Pressed, this, &amp;AFlightC::FlightPathSelected);      \/\/ We have 2 versions of the rotation bindings to handle different kinds of devices differently     \/\/ \"turn\" handles devices that provide an absolute delta, such as a mouse.     \/\/ \"turnrate\" is for devices that we choose to treat as a rate of change, such as an analog joystick     PlayerInputComponent->BindAxis(\"Turn\", this, &amp;APawn::AddControllerYawInput);     PlayerInputComponent->BindAxis(\"TurnRate\", this, &amp;AFlightC::TurnAtRate);     PlayerInputComponent->BindAxis(\"LookUp\", this, &amp;APawn::AddControllerPitchInput);     PlayerInputComponent->BindAxis(\"LookUpRate\", this, &amp;AFlightC::LookUpAtRate);      \/\/ handle touch devices     PlayerInputComponent->BindTouch(IE_Pressed, this, &amp;AFlightC::TouchStarted);     PlayerInputComponent->BindTouch(IE_Released, this, &amp;AFlightC::TouchStopped);  }  void AFlightC::TickTimeline(float Value) {     float SplineLength = ActiveSplineComponent->GetSplineLength();     \/\/ Get the new location based on the provided values from the timeline.     \/\/ The reason we're multiplying Value with SplineLength is because all our designed curves in the UE4 editor have a time range of 0 - X.     \/\/ Where X is the total flight time     FVector NewLocation = ActiveSplineComponent->GetLocationAtDistanceAlongSpline(Value * SplineLength, ESplineCoordinateSpace::World);      SetActorLocation(NewLocation);      FRotator NewRotation = ActiveSplineComponent->GetRotationAtDistanceAlongSpline(Value * SplineLength, ESplineCoordinateSpace::World);      \/\/We're not interested in the pitch value of the above rotation so we make sure to set it to zero     NewRotation.Pitch = 0;     SetActorRotation(NewRotation);      \/\/ \u0435\u0449\u0435 \u043d\u0435 \u0441\u043e\u0437\u0434\u0430\u043d\u043e, \u0441\u043c. 4 \u043f\u0443\u043d\u043a\u0442      CaptureActor->TestFunc(NewLocation);  }  FVector GetSplinePointLocationInConstructionScript(USplineComponent* SplineComponent, int32 PointIndex) { \/\/ Check if the SplineComponent is valid if (!SplineComponent) {     UE_LOG(LogTemp, Error, TEXT(\"Invalid SplineComponent\"));     return FVector::ZeroVector; }  \/\/ Check if the PointIndex is valid if (PointIndex &lt; 0 || PointIndex >= SplineComponent->GetNumberOfSplinePoints()) {     UE_LOG(LogTemp, Error, TEXT(\"Invalid PointIndex\"));     return FVector::ZeroVector; }  \/\/ Get the location of the spline point FVector SplinePointLocation = SplineComponent->GetLocationAtSplinePoint(PointIndex, ESplineCoordinateSpace::World); return SplinePointLocation; }   void AFlightC::OnFlightBoxColliderOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult&amp; SweepResult) {     if (OtherActor->IsA&lt;AFlightActor>())     {         \/\/Store a reference of the nearby flight stop actor         ActiveFlightActor = Cast&lt;AFlightActor>(OtherActor);     } } void AFlightC::FlightPathSelected() {     GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Orange, TEXT(\"KLICK BUTTON\"));      \/\/ get actor in scene by class     AActor* FoundActor = UGameplayStatics::GetActorOfClass(GetWorld(), AFlightActor::StaticClass());     AFlightActor* GameManager = Cast&lt;AFlightActor>(FoundActor);      ActiveFlightActor = Cast&lt;AFlightActor>(GameManager);      if (ActiveFlightActor)     {         \/\/Get the next flight path's spline component and update the flight timeline with the corresponding curve         ActiveSplineComponent = ActiveFlightActor->GetFlightSplineComp();         UpdateFlightTimeline(ActiveFlightActor->GetFlightCurve());     } }  void AFlightC::UpdateFlightTimeline(UCurveFloat* CurveFloatToBind) {     UE_LOG(LogTemp, Warning, TEXT(\"...UpdateFlightTimeline\"));     \/\/Initialize a timeline     FlightTimeline = FTimeline();      FOnTimelineFloat ProgressFunction;      \/\/Bind the function that ticks the timeline     ProgressFunction.BindUFunction(this, FName(\"TickTimeline\"));      \/\/Assign the provided curve and progress function for our timeline     FlightTimeline.AddInterpFloat(CurveFloatToBind, ProgressFunction);     FlightTimeline.SetLooping(false);     FlightTimeline.PlayFromStart();      \/\/Set the timeline's length to match the last key frame based on the given curve     FlightTimeline.SetTimelineLengthMode(TL_LastKeyFrame);      \/\/The ResetActiveFlightActor executes when the timeline finishes.     \/\/By calling ResetActiveFlightActor at the end of the timeline we make sure to reset any invalid references on ActiveFlightActor     FOnTimelineEvent TimelineEvent;     TimelineEvent.BindUFunction(this, FName(\"ResetActiveFlightActor\"));     FlightTimeline.SetTimelineFinishedFunc(TimelineEvent); }  void AFlightC::ResetActiveFlightActor() {     UE_LOG(LogTemp, Warning, TEXT(\"...ResetActiveFlightActor\")); \/\/Display     ActiveFlightActor = nullptr; }  void AFlightC::TouchStarted(ETouchIndex::Type FingerIndex, FVector Location) {     \/\/ jump, but only on the first touch     if (FingerIndex == ETouchIndex::Touch1)     {         Jump();     } }  void AFlightC::TouchStopped(ETouchIndex::Type FingerIndex, FVector Location) {     if (FingerIndex == ETouchIndex::Touch1)     {         StopJumping();     } }  void AFlightC::TurnAtRate(float Rate) {     \/\/ calculate delta for this frame from the rate information     AddControllerYawInput(Rate * BaseTurnRate * GetWorld()->GetDeltaSeconds()); }  void AFlightC::LookUpAtRate(float Rate) {     \/\/ calculate delta for this frame from the rate information     AddControllerPitchInput(Rate * BaseLookUpRate * GetWorld()->GetDeltaSeconds()); }  void AFlightC::MoveForward(float Value) {     if ((Controller != NULL) &amp;&amp; (Value != 0.0f))     {         \/\/ find out which way is forward         const FRotator Rotation = Controller->GetControlRotation();         const FRotator YawRotation(0, Rotation.Yaw, 0);          \/\/ get forward vector         const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);         AddMovementInput(Direction, Value);     } }  void AFlightC::MoveRight(float Value) {     if ( (Controller != NULL) &amp;&amp; (Value != 0.0f) )     {         \/\/ find out which way is right         const FRotator Rotation = Controller->GetControlRotation();         const FRotator YawRotation(0, Rotation.Yaw, 0);          \/\/ get right vector         const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);         \/\/ add movement in that direction         AddMovementInput(Direction, Value);     } } <\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u044e \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u0441apsule \u043a \u043a\u043b\u0430\u0441\u0441\u0443 character, \u044d\u0442\u043e \u0437\u043e\u043d\u0430 \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043f\u0440\u0438 \u043d\u0430\u0436\u0430\u0442\u0438\u0438 \u043d\u0430 \u043a\u043d\u043e\u043f\u043a\u0443 \u043d\u0430\u0447\u043d\u0451\u0442\u0441\u044f \u0434\u0432\u0438\u0436\u0435\u043d\u0438\u0435, \u043f\u043e spline \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u043e\u043c \u0432 <strong>\u0437\u0430\u0434\u0430\u0447\u0430 2<\/strong>. \u041a \u043e\u0431\u044a\u0435\u043a\u0442\u0443<strong> <\/strong>\u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u044e \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u044e\u0449\u0435\u0439 \u043a\u0430\u043c\u0435\u0440\u044b \u0438\u0437 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0439<strong> \u0437\u0430\u0434\u0430\u0447\u0430 4<\/strong>.<strong> <\/strong>\u0412 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 <em>TickTimeline() <\/em>\u0438\u0437\u043c\u0435\u043d\u044f\u044e \u0440\u0430\u0441\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435<em> <\/em>character \u043f\u043e\u043b\u0443\u0447\u0430\u044f \u0434\u0430\u043d\u043d\u044b\u0435 \u0438\u0437 spline \u0432 n-\u0448\u0430\u0433\u0435<em>, <\/em>\u0438<em> <\/em>\u0432\u044b\u043f\u043e\u043b\u043d\u044f\u044e <em>CaptureActor->TestFunc(NewLocation), <\/em>\u043f\u0435\u0440\u0435\u0434\u0430\u0432 \u0442\u0435\u043a\u0443\u0449\u0438\u0435 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b<em> <\/em>FlightC \u0432 CaptureManager (<strong>\u0437\u0430\u0434\u0430\u0447\u0430 4<\/strong>)<em>.<\/em><\/p>\n<h2>4. \u0421\u043e\u0445\u0440\u0430\u043d\u044f\u0442\u044c \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0441 \u043a\u0430\u043c\u0435\u0440\u044b<\/h2>\n<p>\u0421 \u044d\u0442\u043e\u0439 \u0447\u0430\u0441\u0442\u044c\u044e \u043c\u043d\u0435 \u043f\u043e\u0432\u0435\u0437\u043b\u043e, \u0440\u0430\u0431\u043e\u0447\u0438\u0439 \u043a\u043e\u0434, \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u043c\u0438 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u0430\u043c\u0438 \u0432 \u0440\u0430\u0437\u043d\u044b\u0445 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044f\u0445, \u0432\u044b\u0431\u0440\u0430\u043b \u044d\u0442\u043e\u0442 <a href=\"https:\/\/github.com\/TimmHess\/UnrealImageCapture\" rel=\"noopener noreferrer nofollow\">https:\/\/github.com\/TimmHess\/UnrealImageCapture<\/a>. \u0410\u0432\u0442\u043e\u0440 \u0440\u0435\u0448\u0435\u043d\u0438\u044f, \u043f\u043e\u0437\u0430\u0431\u043e\u0442\u0438\u043b\u0441\u044f \u043e \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u043c \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u0438, \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f, \u0447\u0442\u043e \u043e\u0447\u0435\u043d\u044c \u0445\u043e\u0440\u043e\u0448\u043e. \u041a \u044d\u0442\u043e\u043c\u0443 \u043c\u043e\u043c\u0435\u043d\u0442\u0443 \u043d\u0430\u043a\u043e\u043f\u0438\u043b\u043e\u0441\u044c \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u043e\u043f\u044b\u0442\u0430 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 unreal \u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043d\u0443\u0436\u043d\u044b\u0445 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u0432 \u043d\u0435 \u0441\u043e\u0437\u0434\u0430\u043b\u043e \u0442\u0440\u0443\u0434\u043d\u043e\u0441\u0442\u0435\u0439. \u041e\u0434\u0438\u043d \u0432\u0437\u0433\u043b\u044f\u0434 \u043d\u0430 \u043a\u043e\u0434 \u0438 \u0443\u0436\u0435 \u0435\u0441\u0442\u044c \u043f\u043e\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u0447\u0442\u043e \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442&#8230;<\/p>\n<details class=\"spoiler\">\n<summary>CaptureManager.h<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"cpp\">\/\/ Fill out your copyright notice in the Description page of Project Settings.  #pragma once  class ASceneCapture2D; class UMaterial;  #include \"CoreMinimal.h\" #include \"GameFramework\/Actor.h\" #include \"Containers\/Queue.h\" #include \"CaptureManager.generated.h\"   USTRUCT() struct FRenderRequestStruct{     GENERATED_BODY()      TArray&lt;FColor> Image;     FRenderCommandFence RenderFence;      FRenderRequestStruct(){      } };   USTRUCT() struct FFloatRenderRequestStruct{     GENERATED_BODY()      TArray&lt;FFloat16Color> Image;     FRenderCommandFence RenderFence;      FFloatRenderRequestStruct(){      } };   UCLASS(Blueprintable) class ABOVE2_API  ACaptureManager : public AActor {     GENERATED_BODY()  public:     \/\/ Sets default values for this actor's properties      ACaptureManager();      \/\/ Captured Data Sub-Directory Name     UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=\"Capture\")     FString SubDirectoryName = \"\";      UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=\"Capture\")     int NumDigits = 6;      UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=\"Capture\")     int FrameWidth = 640;     UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=\"Capture\")     int FrameHeight = 480;      \/\/ If not UsePNG, JPEG format is used (For Non-Color purposes PNG is necessary, elsewise compression will mess with labels!)     UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=\"Capture\")     bool UsePNG = false;      UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=\"Capture\")     bool UseFloat = false;      \/\/ Color Capture Components     UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=\"Capture\")     ASceneCapture2D* CaptureComponent;      \/\/UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=\"Capture\")     \/\/ASceneCapture2D* SegmentationCapture = nullptr;      \/\/ PostProcessMaterial used for segmentation     UPROPERTY(EditAnywhere, Category=\"Capture\")     UMaterial* PostProcessMaterial = nullptr;      UPROPERTY(EditAnywhere, Category=\"Logging\")     bool VerboseLogging = false;  protected:     \/\/ RenderRequest Queue     TQueue&lt;FRenderRequestStruct*> RenderRequestQueue;      \/\/ FloatRenderRequest Queue     TQueue&lt;FFloatRenderRequestStruct*> RenderFloatRequestQueue;     int ImgCounter = 0;  protected:     \/\/ Called when the game starts or when spawned     virtual void BeginPlay() override;      void SetupCaptureComponent();      \/\/ Creates an async task that will save the captured image to disk     void RunAsyncImageSaveTask(TArray64&lt;uint8> Image, FString ImageName);     FString ToStringWithLeadingZeros(int32 Integer, int32 MaxDigits);  public:     \/\/ Called every frame     virtual void Tick(float DeltaTime) override;      UFUNCTION(BlueprintCallable, Category = \"ImageCapture\")     void CaptureNonBlocking();      UFUNCTION(BlueprintCallable, Category = \"ImageCapture\")     void CaptureFloatNonBlocking(); public:     UFUNCTION()     void TestFunc(FVector NewLocation); \/\/FVector NewLocation  };   class AsyncSaveImageToDiskTask : public FNonAbandonableTask{     public:         AsyncSaveImageToDiskTask(TArray64&lt;uint8> Image, FString ImageName);         ~AsyncSaveImageToDiskTask();      \/\/ Required by UE4!     FORCEINLINE TStatId GetStatId() const{         RETURN_QUICK_DECLARE_CYCLE_STAT(AsyncSaveImageToDiskTask, STATGROUP_ThreadPoolAsyncTasks);     }  protected:     TArray64&lt;uint8> ImageCopy;     FString FileName = \"\";  public:     void DoWork(); }; <\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>CaptureManager.cpp<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"cpp\">\/\/ Fill out your copyright notice in the Description page of Project Settings.  #include \"CaptureManager.h\" #include \"Runtime\/Engine\/Classes\/Engine\/Engine.h\"  #include \"Engine\/SceneCapture2D.h\" #include \"Components\/SceneCaptureComponent2D.h\" #include \"Engine\/TextureRenderTarget2D.h\" #include \"Kismet\/GameplayStatics.h\" #include \"ShowFlags.h\"  #include \"Materials\/Material.h\"  #include \"RHICommandList.h\"  #include \"ImageWrapper\/Public\/IImageWrapper.h\" #include \"ImageWrapper\/Public\/IImageWrapperModule.h\"  #include \"ImageUtils.h\"  #include \"Modules\/ModuleManager.h\" #include \"FlightC.h\"  #include \"Misc\/FileHelper.h\"  \/\/ Sets default values ACaptureManager::ACaptureManager() {     \/\/ Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.     PrimaryActorTick.bCanEverTick = true;  }  \/\/ Called when the game starts or when spawned void ACaptureManager::BeginPlay() {     Super::BeginPlay();      if(CaptureComponent){ \/\/ nullptr check         SetupCaptureComponent();         UE_LOG(LogTemp, Warning, TEXT(\"CaptureComponent.... %s\"), *CaptureComponent->GetFName().ToString());     } else{         UE_LOG(LogTemp, Error, TEXT(\"No CaptureComponent set!\"));     }  }  \/\/ Called every frame void ACaptureManager::Tick(float DeltaTime) {     Super::Tick(DeltaTime);     if(UseFloat){         \/\/ READ FLOAT IMAGE         if(!RenderFloatRequestQueue.IsEmpty()){             \/\/ Peek the next RenderRequest from queue             FFloatRenderRequestStruct* nextRenderRequest = nullptr;             RenderFloatRequestQueue.Peek(nextRenderRequest);              if(nextRenderRequest){                 if(nextRenderRequest->RenderFence.IsFenceComplete()){                     \/\/ Load the image wrapper module                     IImageWrapperModule&amp; ImageWrapperModule = FModuleManager::LoadModuleChecked&lt;IImageWrapperModule>(FName(\"ImageWrapper\"));                      FString fileName = \"\";                     fileName = FPaths::ProjectSavedDir() + SubDirectoryName + \"\/img\" + \"_\" + ToStringWithLeadingZeros(ImgCounter, NumDigits);                     fileName += \".exr\"; \/\/ Add file ending                      static TSharedPtr&lt;IImageWrapper> imageWrapper = ImageWrapperModule.CreateImageWrapper(EImageFormat::EXR); \/\/EImageFormat::PNG \/\/EImageFormat::JPEG                     imageWrapper->SetRaw(nextRenderRequest->Image.GetData(), nextRenderRequest->Image.GetAllocatedSize(), FrameWidth, FrameHeight, ERGBFormat::RGBA, 16);                     const TArray64&lt;uint8>&amp; PngData = imageWrapper->GetCompressed(0);                     FFileHelper::SaveArrayToFile(PngData, *fileName);                       \/\/ Delete the first element from RenderQueue                     RenderFloatRequestQueue.Pop();                     delete nextRenderRequest;                      ImgCounter += 1;                 }             }         }     }     \/\/ READ UINT8 IMAGE     \/\/ Read pixels once RenderFence is completed     else{         if(!RenderRequestQueue.IsEmpty()){             \/\/ Peek the next RenderRequest from queue             FRenderRequestStruct* nextRenderRequest = nullptr;             RenderRequestQueue.Peek(nextRenderRequest);             if(nextRenderRequest){ \/\/nullptr check                 if(nextRenderRequest->RenderFence.IsFenceComplete()){ \/\/ Check if rendering is done, indicated by RenderFence                     \/\/ Load the image wrapper module                     IImageWrapperModule&amp; ImageWrapperModule = FModuleManager::LoadModuleChecked&lt;IImageWrapperModule>(FName(\"ImageWrapper\"));                     \/\/ Decide storing of data, either jpeg or png                     FString fileName = \"\";                     if(UsePNG){                         \/\/Generate image name                         fileName = FPaths::ProjectSavedDir() + SubDirectoryName + \"\/img\" + \"_\" + ToStringWithLeadingZeros(ImgCounter, NumDigits);                         fileName += \".png\"; \/\/ Add file ending                          \/\/ Prepare data to be written to disk                         static TSharedPtr&lt;IImageWrapper> imageWrapper = ImageWrapperModule.CreateImageWrapper(EImageFormat::PNG); \/\/EImageFormat::PNG \/\/EImageFormat::JPEG                         imageWrapper->SetRaw(nextRenderRequest->Image.GetData(), nextRenderRequest->Image.GetAllocatedSize(), FrameWidth, FrameHeight, ERGBFormat::BGRA, 8);                         const TArray64&lt;uint8>&amp; ImgData = imageWrapper->GetCompressed(5);                         \/\/const TArray&lt;uint8>&amp; ImgData =  static_cast&lt;TArray&lt;uint8, FDefaultAllocator>> (imageWrapper->GetCompressed(5));                         RunAsyncImageSaveTask(ImgData, fileName);                     } else{                         \/\/ Generate image name                         fileName = FPaths::ProjectSavedDir() + SubDirectoryName + \"\/img\" + \"_\" + ToStringWithLeadingZeros(ImgCounter, NumDigits);                         fileName += \".jpeg\"; \/\/ Add file ending                          \/\/ Prepare data to be written to disk                         static TSharedPtr&lt;IImageWrapper> imageWrapper = ImageWrapperModule.CreateImageWrapper(EImageFormat::JPEG); \/\/EImageFormat::PNG \/\/EImageFormat::JPEG                         imageWrapper->SetRaw(nextRenderRequest->Image.GetData(), nextRenderRequest->Image.GetAllocatedSize(), FrameWidth, FrameHeight, ERGBFormat::BGRA, 8);                         const TArray64&lt;uint8>&amp; ImgData = imageWrapper->GetCompressed(0);                         \/\/const TArray&lt;uint8>&amp; ImgData = static_cast&lt;TArray&lt;uint8, FDefaultAllocator>> (imageWrapper->GetCompressed(0));                         RunAsyncImageSaveTask(ImgData, fileName);                     }                     if(VerboseLogging &amp;&amp; !fileName.IsEmpty()){                         UE_LOG(LogTemp, Warning, TEXT(\"%f\"), *fileName);                     }                      ImgCounter += 1;                      \/\/ Delete the first element from RenderQueue                     RenderRequestQueue.Pop();                     delete nextRenderRequest;                 }             }         }     } }  void ACaptureManager::SetupCaptureComponent(){     if(!IsValid(CaptureComponent)){         UE_LOG(LogTemp, Error, TEXT(\"SetupCaptureComponent: CaptureComponent is not valid!\"));         return;     }      \/\/ Create RenderTargets     UTextureRenderTarget2D* renderTarget2D = NewObject&lt;UTextureRenderTarget2D>();      \/\/ Float Capture     if(UseFloat){         renderTarget2D->RenderTargetFormat = ETextureRenderTargetFormat::RTF_RGBA32f;         renderTarget2D->InitCustomFormat(FrameWidth, FrameHeight, PF_FloatRGBA, true); \/\/ PF_B8G8R8A8 disables HDR which will boost storing to disk due to less image information         UE_LOG(LogTemp, Warning, TEXT(\"Set Render Format for DepthCapture..\"));     }     \/\/ Color Capture     else{         renderTarget2D->RenderTargetFormat = ETextureRenderTargetFormat::RTF_RGBA8; \/\/8-bit color format         renderTarget2D->InitCustomFormat(FrameWidth, FrameHeight, PF_B8G8R8A8, true); \/\/ PF... disables HDR, which is most important since HDR gives gigantic overhead, and is not needed!         UE_LOG(LogTemp, Warning, TEXT(\"Set Render Format for Color-Like-Captures\"));     }      renderTarget2D->bGPUSharedFlag = true; \/\/ demand buffer on GPU      \/\/ Assign RenderTarget     CaptureComponent->GetCaptureComponent2D()->TextureTarget = renderTarget2D;     \/\/ Set Camera Properties     CaptureComponent->GetCaptureComponent2D()->CaptureSource = ESceneCaptureSource::SCS_FinalColorLDR;     CaptureComponent->GetCaptureComponent2D()->TextureTarget->TargetGamma = GEngine->GetDisplayGamma();     CaptureComponent->GetCaptureComponent2D()->ShowFlags.SetTemporalAA(true);     \/\/ lookup more showflags in the UE4 documentation..      \/\/ Assign PostProcess Material if assigned     if(PostProcessMaterial){ \/\/ check nullptr         CaptureComponent->GetCaptureComponent2D()->AddOrUpdateBlendable(PostProcessMaterial);     } else {         UE_LOG(LogTemp, Log, TEXT(\"No PostProcessMaterial is assigend\"));     }     UE_LOG(LogTemp, Warning, TEXT(\"Initialized RenderTarget!\"));       FVector startPos = FVector(0.0f,0.0f,20.0f);     CaptureComponent->SetActorLocation(startPos);     UE_LOG(LogTemp, Error, TEXT(\"New ActorLocation ------>%s\"), *CaptureComponent->GetActorLocation().ToString()); }  void ACaptureManager::CaptureNonBlocking(){     if(!IsValid(CaptureComponent)){         UE_LOG(LogTemp, Error, TEXT(\"CaptureColorNonBlocking: CaptureComponent was not valid!\"));         return;     }     UE_LOG(LogTemp, Warning, TEXT(\"Entering: CaptureNonBlocking\"));     CaptureComponent->GetCaptureComponent2D()->TextureTarget->TargetGamma = 1.2f;     \/\/ CaptureComponent->GetCaptureComponent2D()->TextureTarget->TargetGamma = GEngine->GetDisplayGamma();      \/\/ Get RenderConterxt     FTextureRenderTargetResource* renderTargetResource = CaptureComponent->GetCaptureComponent2D()->TextureTarget->GameThread_GetRenderTargetResource();     UE_LOG(LogTemp, Warning, TEXT(\"Got display gamma\"));     struct FReadSurfaceContext{         FRenderTarget* SrcRenderTarget;         TArray&lt;FColor>* OutData;         FIntRect Rect;         FReadSurfaceDataFlags Flags;     };     UE_LOG(LogTemp, Warning, TEXT(\"Inited ReadSurfaceContext\"));     \/\/ Init new RenderRequest     FRenderRequestStruct* renderRequest = new FRenderRequestStruct();     UE_LOG(LogTemp, Warning, TEXT(\"inited renderrequest\"));      \/\/ Setup GPU command     FReadSurfaceContext readSurfaceContext = {         renderTargetResource,         &amp;(renderRequest->Image),         FIntRect(0,0,renderTargetResource->GetSizeXY().X, renderTargetResource->GetSizeXY().Y),         FReadSurfaceDataFlags(RCM_UNorm, CubeFace_MAX)     };     UE_LOG(LogTemp, Warning, TEXT(\"GPU Command complete\"));      \/\/ Send command to GPU     \/* Up to version 4.22 use this     ENQUEUE_UNIQUE_RENDER_COMMAND_ONEPARAMETER(         SceneDrawCompletion,\/\/ReadSurfaceCommand,         FReadSurfaceContext, Context, readSurfaceContext,     {         RHICmdList.ReadSurfaceData(             Context.SrcRenderTarget->GetRenderTargetTexture(),             Context.Rect,             *Context.OutData,             Context.Flags         );     });     *\/     \/\/ Above 4.22 use this     ENQUEUE_RENDER_COMMAND(SceneDrawCompletion)(     [readSurfaceContext](FRHICommandListImmediate&amp; RHICmdList){         RHICmdList.ReadSurfaceData(             readSurfaceContext.SrcRenderTarget->GetRenderTargetTexture(),             readSurfaceContext.Rect,             *readSurfaceContext.OutData,             readSurfaceContext.Flags         );     });      \/\/ Notifiy new task in RenderQueue     RenderRequestQueue.Enqueue(renderRequest);      \/\/ Set RenderCommandFence     renderRequest->RenderFence.BeginFence(); }  void ACaptureManager::CaptureFloatNonBlocking(){     \/\/ Initial Check     if(!UseFloat){         UE_LOG(LogTemp, Error, TEXT(\"Called CaptureFloatNonBlocking but UseFloat is false! Will omit this call to prevent crashes!\"));         return;     }      \/\/ Get RenderContext     FTextureRenderTargetResource* renderTargetResource = CaptureComponent->GetCaptureComponent2D()->TextureTarget->GameThread_GetRenderTargetResource();      \/\/ Read the render target surface data back.     struct FReadSurfaceFloatContext     {         FRenderTarget* SrcRenderTarget;         TArray&lt;FFloat16Color>* OutData;         FIntRect Rect;         ECubeFace CubeFace;     };      \/\/ Init new RenderRequest     FFloatRenderRequestStruct* renderFloatRequest = new FFloatRenderRequestStruct();      \/\/ Setup GPU command     \/\/TArray&lt;FFloat16Color> SurfaceData;     FReadSurfaceFloatContext Context = {         renderTargetResource,         &amp;(renderFloatRequest->Image),         \/\/&amp;SurfaceData,         FIntRect(0, 0, FrameWidth, FrameHeight),         ECubeFace::CubeFace_MAX \/\/no cubeface     };      ENQUEUE_RENDER_COMMAND(ReadSurfaceFloatCommand)(         [Context](FRHICommandListImmediate&amp; RHICmdList) {             RHICmdList.ReadSurfaceFloatData(                 Context.SrcRenderTarget->GetRenderTargetTexture(),                 Context.Rect,                 *Context.OutData,                 Context.CubeFace,                 0,                 0                 );         });      RenderFloatRequestQueue.Enqueue(renderFloatRequest);     renderFloatRequest->RenderFence.BeginFence(); }  FString ACaptureManager::ToStringWithLeadingZeros(int32 Integer, int32 MaxDigits){     FString result = FString::FromInt(Integer);     int32 stringSize = result.Len();     int32 stringDelta = MaxDigits - stringSize;     if(stringDelta &lt; 0){         UE_LOG(LogTemp, Error, TEXT(\"MaxDigits of ImageCounter Overflow!\"));         return result;     }     \/\/FIXME: Smarter function for this..     FString leadingZeros = \"\";     for(size_t i=0;i&lt;stringDelta;i++){         leadingZeros += \"0\";     }     result = leadingZeros + result;      return result; }  void ACaptureManager::RunAsyncImageSaveTask(TArray64&lt;uint8> Image, FString ImageName){     UE_LOG(LogTemp, Warning, TEXT(\"Running Async Task\"));     (new FAutoDeleteAsyncTask&lt;AsyncSaveImageToDiskTask>(Image, ImageName))->StartBackgroundTask(); }  void ACaptureManager::TestFunc(FVector NewLocation){     UE_LOG(LogTemp, Warning, TEXT(\"ACaptureManager's Name is %s\"), *this->GetFName().ToString());     UE_LOG(LogTemp, Warning, TEXT(\"Location CaptureManager %s\"), *this->CaptureComponent->GetActorLocation().ToString());     CaptureComponent->SetActorLocation(NewLocation);     CaptureNonBlocking(); }  \/* ******************************************************************* *\/  AsyncSaveImageToDiskTask::AsyncSaveImageToDiskTask(TArray64&lt;uint8> Image, FString ImageName){     ImageCopy = Image;     FileName = ImageName; }  AsyncSaveImageToDiskTask::~AsyncSaveImageToDiskTask(){     UE_LOG(LogTemp, Warning, TEXT(\"AsyncTaskDone\")); }  void AsyncSaveImageToDiskTask::DoWork(){     UE_LOG(LogTemp, Warning, TEXT(\"Starting Work\"));     FFileHelper::SaveArrayToFile(ImageCopy, *FileName);     UE_LOG(LogTemp, Log, TEXT(\"Stored Image: %s\"), *FileName); } <\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u041a\u043e\u043c\u043f\u0438\u043b\u0438\u0440\u0443\u044e, \u043d\u0430\u0441\u043b\u0430\u0436\u0434\u0430\u044e\u0441\u044c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u043c:<\/p>\n<div class=\"tm-iframe_temp\" data-src=\"https:\/\/embedd.srv.habr.com\/iframe\/650b4ecc2fbe8876a60f1195\" data-style=\"\" id=\"650b4ecc2fbe8876a60f1195\" width=\"\"><\/div>\n<p>\u0412\u043d\u0438\u043c\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0441 \u043e\u043f\u044b\u0442\u043e\u043c \u0432 unreal, \u0441\u043c\u043e\u0436\u0435\u0442 \u0443\u043f\u0440\u0435\u043a\u043d\u0443\u0442\u044c \u043c\u0435\u043d\u044f: <em>\u043d\u0435 \u043f\u043e\u043a\u0430\u0437\u0430\u043b \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 curve. <\/em>\u041e\u0442\u0432\u0435\u0442 \u043d\u0430 \u0443\u043f\u0440\u0451\u043a, \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0432 <em>FlightActor.cpp<\/em>:<\/p>\n<pre><code class=\"cpp\">    FlightCurve = NewObject&lt;UCurveFloat>(this, TEXT(\"DynamicUCurveFloat\"));     FKeyHandle KeyHandle = FlightCurve->FloatCurve.AddKey(0.0f, 0.1f);     FlightCurve->FloatCurve.SetKeyInterpMode(KeyHandle, ERichCurveInterpMode::RCIM_Cubic, true);     FKeyHandle KeyHandle2 = FlightCurve->FloatCurve.AddKey(100.0f, 1300.0f);     FlightCurve->FloatCurve.SetKeyInterpMode(KeyHandle2, ERichCurveInterpMode::RCIM_Cubic, true);<\/code><\/pre>\n<p>\u0418\u0442\u043e\u0433\u043e\u0432\u044b\u0439 \u0432\u0430\u0440\u0438\u0430\u043d\u0442 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043d\u043e\u0439 \u0437\u0430\u0434\u0430\u0447\u0435. \u041f\u0440\u0438\u043e\u0431\u0440\u0451\u043b \u043d\u043e\u0432\u044b\u0435 \u0437\u043d\u0430\u043d\u0438\u044f \u0438 \u0433\u043e\u0442\u043e\u0432 \u043a \u043d\u043e\u0432\u044b\u043c \u0432\u044b\u0437\u043e\u0432\u0430\u043c. \u0421\u0442\u0430\u0442\u044c\u044f \u0432 \u043f\u0435\u0440\u0432\u0443\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u043c\u0430\u044f\u043a \u0434\u043b\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432 \u0441 \u0441\u0445\u043e\u0436\u0438\u043c\u0438 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0430\u043c\u0438, \u0442\u0435 \u043a\u0442\u043e \u043f\u043e\u043d\u044f\u043b \u0447\u0442\u043e &#171;\u0442\u0443\u0442 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442&#187;, \u0438 \u0433\u0434\u0435 \u044d\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0442\u044c\u0441\u044f, \u043f\u0438\u0448\u0438\u0442\u0435 \u0432 \u043b\u0441, \u0434\u043b\u044f \u0432\u0430\u0441 \u0435\u0441\u0442\u044c \u0440\u0430\u0431\u043e\u0442\u0430, \u043d\u0435\u0432\u0435\u0440\u043e\u044f\u0442\u043d\u044b\u0445 \u0441\u0443\u043c\u043c \u0443 \u043d\u0430\u0441 \u043d\u0435\u0442, \u0441\u043a\u0440\u043e\u043c\u043d\u043e, \u043d\u043e \u0441\u0432\u043e\u0435\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u0430\u044f \u043e\u043f\u043b\u0430\u0442\u0430 \u0433\u0430\u0440\u0430\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u0430!!!<\/p>\n<\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/762136\/\"> https:\/\/habr.com\/ru\/articles\/762136\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u042d\u0442\u0430 \u0441\u0442\u0430\u0442\u044c\u044f, \u043c\u043e\u0439 \u043a\u043e\u043d\u0441\u043f\u0435\u043a\u0442, \u0441\u0438\u0433\u043d\u0430\u043b\u044c\u043d\u044b\u0439 \u0444\u043b\u0430\u0433, \u0438\u043b\u0438 \u043e\u0447\u0435\u0440\u0435\u0434\u043d\u0430\u044f \u0442\u0440\u0435\u043d\u0438\u0440\u043e\u0432\u043a\u0430 \u0438\u0437\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0441\u0432\u043e\u0438\u0445 \u043c\u044b\u0441\u043b\u0435\u0439? \u0412 \u0441\u0438\u043b\u0443 \u043e\u0431\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u0441\u0442\u0432\u0430, \u043f\u0440\u0438\u043a\u043e\u0441\u043d\u0443\u043b\u0441\u044f \u043a unreal, \u0437\u0430\u043c\u0435\u0447\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u0432 &#171;\u0443\u043c\u0435\u043b\u044b\u0445 \u0440\u0443\u043a\u0430\u0445&#187;, \u043c\u043d\u043e\u0433\u043e \u043e\u0434 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u043e \u0441\u0435\u043c\u0443 \u0442\u0432\u043e\u0440\u0435\u043d\u0438\u044e \u0447\u0435\u043b\u043e\u0432\u0435\u0447\u0435\u0441\u043a\u043e\u0439 \u043c\u044b\u0441\u043b\u0438, \u0442\u0430\u043a \u0447\u0442\u043e \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u0441 \u043d\u0438\u043c \u0431\u043e\u043b\u044c\u0448\u0430\u044f \u0447\u0435\u0441\u0442\u044c \u0434\u043b\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430. \u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0438\u0433\u0440, \u0432\u0438\u0437\u0443\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f, \u0438\u0441\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u043d\u0438\u044f, \u043c\u043d\u043e\u0433\u043e \u0432\u0441\u0435\u0433\u043e \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e\u0433\u043e \u0437\u0430\u043b\u043e\u0436\u0435\u043d\u043e \u0432 \u044d\u0442\u043e\u0442 \u043f\u0440\u043e\u0435\u043a\u0442 \u0441 \u043c\u043d\u043e\u0433\u043e\u043b\u0435\u0442\u043d\u0435\u0439 \u0438\u0441\u0442\u043e\u0440\u0438\u0435\u0439 \u0440\u0430\u0437\u0432\u0438\u0442\u0438\u044f. \u041e\u0442\u043a\u0440\u044b\u0442\u043e\u0441\u0442\u044c \u0438 \u0431\u043e\u043b\u044c\u0448\u043e\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u0441\u0442\u0432\u043e, \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u043f\u043e\u043d\u0438\u0436\u0430\u0435\u0442 \u043f\u043e\u0440\u043e\u0433 \u0432\u0445\u043e\u0436\u0434\u0435\u043d\u0438\u044f, \u043a\u043e\u043d\u0435\u0447\u043d\u043e \u0442\u0440\u0438\u0432\u0438\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u043f\u0438\u0441\u0430\u0442\u044c \u0442\u0430\u043a\u043e\u0435, \u043a\u0430\u0436\u0434\u044b\u0439 \u0432\u0442\u043e\u0440\u043e\u0439 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442 \u0441 \u0442\u0430\u043a\u0438\u043c\u0438 \u0445\u0430\u0440\u0430\u043a\u0442\u0435\u0440\u0438\u0441\u0442\u0438\u043a\u0430\u043c\u0438, \u043d\u043e \u044d\u0442\u043e \u0433\u043e\u0432\u043e\u0440\u0438\u0442 \u043e \u043e\u0431\u0449\u0435\u0439 \u0432\u044b\u0441\u043e\u043a\u043e\u0439 \u043f\u043b\u0430\u043d\u043a\u0435 \u043d\u044b\u043d\u0435\u0448\u043d\u0438\u0445 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u0434\u043b\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043b\u044e\u0431\u044b\u0445 \u0442\u0435\u0445\u043d\u043e \u0438\u0437\u0432\u0440\u0430\u0449\u0451\u043d\u043d\u044b\u0445 \u0444\u0430\u043d\u0442\u0430\u0437\u0438\u0439. \u041d\u0435\u0432\u0435\u0440\u043e\u044f\u0442\u043d\u043e\u0435 \u0441\u0442\u0435\u0447\u0435\u043d\u0438\u0435 \u043e\u0431\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u0441\u0442\u0432, \u043f\u043e\u043b\u0443\u0447\u0430\u044e \u0434\u0435\u043d\u044c\u0433\u0438 \u0437\u0430 \u0442\u043e \u0447\u0442\u043e \u0443\u0447\u0443\u0441\u044c \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c \u0441 \u044d\u0442\u0438\u043c \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u043c. <\/p>\n<h3>\u041a\u043e\u0440\u043e\u0442\u043a\u043e \u043e \u0437\u0430\u0434\u0430\u0447\u0435: <\/h3>\n<ol>\n<li>\n<p>\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0444\u0430\u0439\u043b \u0444\u043e\u0442\u043e\u0433\u0440\u0430\u043c\u043c\u0435\u0442\u0440\u0438\u0438 \u043c\u0435\u0441\u0442\u043d\u043e\u0441\u0442\u0438<\/p>\n<\/li>\n<li>\n<p>\u041d\u0430\u0447\u0435\u0440\u0442\u0438\u0442\u044c \u043f\u0443\u0442\u044c<\/p>\n<\/li>\n<li>\n<p>\u041f\u043e \u0437\u0430\u0434\u0430\u043d\u043d\u043e\u043c\u0443 \u043f\u0443\u0442\u0438 \u0434\u0432\u0438\u0433\u0430\u0442\u044c \u043a\u0430\u043c\u0435\u0440\u0443<\/p>\n<\/li>\n<li>\n<p>\u0421\u043e\u0445\u0440\u0430\u043d\u044f\u0442\u044c \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0441 \u043a\u0430\u043c\u0435\u0440\u044b<\/p>\n<\/li>\n<\/ol>\n<blockquote>\n<p>\u0413\u043b\u0430\u0432\u043d\u043e\u0435 \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u0435: \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0431\u043e\u043b\u044c\u0448\u0443\u044e \u0447\u0430\u0441\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u0430 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435\u043c C++, \u0441 \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c blueprint<\/p>\n<\/blockquote>\n<p>\u0418\u0437\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e \u044f \u0441\u0434\u0435\u043b\u0430\u043b \u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u043f\u0440\u043e\u0442\u043e\u0442\u0438\u043f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f tkinter (python) \u0434\u043b\u044f \u0432\u0438\u0437\u0443\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0441\u0432\u043e\u0438\u0445 \u043c\u044b\u0441\u043b\u0435\u0439, \u0432\u043c\u0435\u0441\u0442\u043e \u0444\u0430\u0439\u043b\u0430 \u0444\u043e\u0442\u043e\u0433\u0440\u0430\u043c\u043c\u0435\u0442\u0440\u0438\u0438, \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435.<\/p>\n<figure class=\"full-width\">\n<div><figcaption>\u043f\u0440\u043e\u0441\u0442\u0430\u044f \u0432\u0438\u0437\u0443\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f<\/figcaption><\/div>\n<\/figure>\n<details class=\"spoiler\">\n<summary>\u041a\u043e\u0434 \u0432\u0438\u0437\u0443\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">from tkinter import * from tkinter import filedialog from tkinter import messagebox from tkinter import ttk from PIL import Image, ImageTk import os import random import numpy as np import matplotlib.pyplot as plt import matplotlib.image as mpimg import cv2 import math import time   width_window = 300.0 height_window = 200.0   def crop_rect(img, rect, ix):     print(\"rect!\")     # \u043f\u043e\u0432\u043e\u0440\u043e\u0442 \u0444\u0440\u0430\u0433\u043c\u0435\u043d\u0442\u0430     center, size, angle = rect[0], rect[1], rect[2]     center, size = tuple(map(int, center)), tuple(map(int, size))     height, width = img.shape[0], img.shape[1]     M = cv2.getRotationMatrix2D(center, angle, 1)     img_rot = cv2.warpAffine(img[:,:,:3], M, (width, height))     cut_img = cv2.getRectSubPix(img_rot, size, center)          # \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0447\u0430\u0441\u0442\u044c \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f\/\u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440 \u0432\u044b\u0440\u0435\u0437\u0430\u043d\u043d\u043e\u0433\u043e \u0444\u0440\u0430\u0433\u043c\u0435\u043d\u0442\u0430     cv2.imwrite(f'data\/cut_parts\/{ix}_part.jpg', cut_img)     cv2.imshow('cut_img', cut_img)     time.sleep(0.08)     if cv2.waitKey(1) == ord('q'):         cv2.destroyAllWindows()     return img   class CutTool():     def __init__(self, master):         # \u043d\u0430\u0439\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0433\u043e \u043e\u043a\u043d\u0430         self.parent = master         self.parent.title(\"CutTool\")         self.frame = Frame(self.parent)         self.frame.pack(fill=BOTH, expand=1)         self.parent.resizable(width = True, height = True)          # \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u043a\u0443\u0440\u0441\u043e\u0440\u0430 \u043c\u044b\u0448\u0438         self.STATE = {}         self.STATE['click'] = 0         self.STATE['x'], self.STATE['y'] = 0, 0          # \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b          self.coordList = []         self.running = False                  self.image_container = False                  self.step_size = 20         # ----------------- GUI stuff ---------------------          # \u0433\u043b\u0430\u0432\u043d\u0430\u044f \u043f\u0430\u043d\u0435\u043b\u044c \u0434\u043b\u044f \u043c\u0430\u0440\u043a\u0438\u0440\u043e\u0432\u043a\u0438         self.mainPanel = Canvas(self.frame, cursor='tcross')         self.mainPanel.bind('&lt;ButtonPress-1>',self.start_motor)         self.mainPanel.bind('&lt;ButtonRelease-1>',self.stop_motor)                           self.mainPanel.bind(\"&lt;Motion>\", self.mouseMove)         self.mainPanel.grid(row = 2, column = 1, rowspan = 4, sticky = W+N)          # \u043f\u0430\u043d\u0435\u043b\u044c \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u043d\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u0438 \u043f\u043e \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f\u043c         self.ctrPanel = Frame(self.frame)         self.ctrPanel.grid(row = 6, colum\u043c\u044b\u0448\u0438n = 1, columnspan = 2, sticky = W+E)          # \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043a\u0443\u0440\u0441\u043e\u0440\u0430 \u043c\u044b\u0448\u0438         self.disp = Label(self.ctrPanel, text='')         self.disp.pack(side = RIGHT)                  self.loadImage()              def mouseClick(self, event):         if self.STATE['click'] == 0:             self.STATE['x'], self.STATE['y'] = event.x, event.y         print (\"mouseClick event --->\", self.STATE)      def mouseMove(self, event):         if self.running:             self.draw(event)         self.disp.config(text = 'x: %d, y: %d' %(event.x, event.y))      def start_motor(self, event):         self.running = True         print(\"starting motor...\")          def stop_motor(self, event):         print(\"stopping motor...\")         self.running = False         self.curve_2d(self.coordList)         self.coordList = []      def curve_2d(self, x):         R = len(x)         pts = x         x = np.array(x)         ptdiff = lambda p1, p2: (p1[0]-p2[0], p1[1]-p2[1])          diffs = (ptdiff(p1, p2) for p1, p2 in zip (pts, pts[1:]))         path = sum(math.hypot(*d) for d in  diffs)          img_array = np.array(self.img)         img_array2 = np.array(self.img)         print (img_array.shape)         global img_         for ix, i in enumerate(pts):             if ix == 0: continue             if ix % self.step_size == 0:                                      \"\"\"                 1 \u0443\u0437\u043d\u0430\u0442\u044c \u0443\u0433\u043e\u043b \u043c\u0435\u0436\u0434\u0443 \u043d\u0430\u0441\u0442\u043e\u044f\u0449\u0435\u0439 \u0442\u043e\u0447\u043a\u043e\u0439 \u0438 \u043f\u0440\u043e\u0448\u043b\u043e\u0439 \u043f\u043e \u043e\u0442\u043d\u043e\u0448\u0435\u043d\u0438\u044e \u043a \u043e\u0441\u0438 'x'                 2 \u043f\u0440\u043e\u0432\u0435\u0440\u043d\u0443\u0442\u044c \u043d\u0430 \u044d\u0442\u043e\u0442 \u0443\u0433\u043e\u043b \u0442\u043e\u0447\u043a\u0438 \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u043f\u0440\u044f\u043c\u043e\u0443\u0433\u043e\u043b\u044c\u043d\u0438\u043a\u0430                  \"\"\"                 a = np.array(pts[ix])                 b = np.array(pts[ix-self.step_size])                  ab = a - b                 hypotenuse = np.hypot(ab[0], ab[1])                        cos_A = ab[0]\/hypotenuse                 angle = int(math.degrees(math.acos(cos_A)))                    # \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e\u0435 \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435                              if ab[1] &lt; 0:                     angle = -float(angle)                 else:                     angle = float(angle)                 rect_ = ((float(a[0]), float(a[1])), (height_window, width_window), angle)                 img_array2_ = crop_rect(img_array, rect_, ix)                 # \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u043b\u0438\u043d\u0438\u0438                 box = cv2.boxPoints(rect_)                 box = np.int0(box)                 cv2.drawContours(img_array2, [box], 0, (0,0,255), 2)                 cv2.circle(img_array2, (a[0], a[1]), radius=10, color=(0, 0, 255), thickness=-1)                                img_ = ImageTk.PhotoImage(image=Image.fromarray(img_array2))                 self.mainPanel.itemconfig(self.image_container, image=img_)                   # \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u044c \u043a\u0430\u043d\u0432\u0430\u0441                 self.mainPanel.update()                                 def loadImage(self):         # \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435         self.img = Image.open(\"media-info\/test.png\")         size = self.img.size         self.factor = max(size[0]\/10000., size[1]\/10000., 1.)         self.img = self.img.resize((int(size[0]\/self.factor), int(size[1]\/self.factor)))         print (int(size[0]\/self.factor), int(size[1]\/self.factor))         self.tkimg = ImageTk.PhotoImage(self.img)         self.mainPanel.config(width = max(self.tkimg.width(), 400), height = max(self.tkimg.height(), 400))         self.image_container = self.mainPanel.create_image(0, 0, image = self.tkimg, anchor=NW)              def draw(self, event):         self.coordList.append((event.x, event.y))         self.mainPanel.create_oval(event.x - 3,                                   event.y - 3,                                   event.x + 3,                                   event.y + 3,                                   fill=\"red\", outline=\"red\")           if __name__ == '__main__':     root = Tk()     tool = CutTool(root)     root.resizable(width =  True, height = True)     root.mainloop()<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p><strong>\u041f\u043b\u0430\u043d \u0435\u0441\u0442\u044c, \u0432\u0438\u0437\u0443\u0430\u043b\u044c\u043d\u044b\u0439 \u043e\u0440\u0438\u0435\u043d\u0442\u0438\u0440 \u0435\u0441\u0442\u044c, \u0432\u0440\u0435\u043c\u044f \u043f\u0440\u0438\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0439!<\/strong> \u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 unreal 4.27 \u0437\u0430\u043d\u044f\u043b\u0430 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0434\u043d\u0435\u0439 \u0438\u0437-\u0437\u0430 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0439 \u043d\u0430\u043b\u043e\u0436\u0435\u043d\u043d\u044b\u0445 epic \u043d\u0430 \u043c\u043e\u0439 \u0440\u0435\u0433\u0438\u043e\u043d \u043e\u0431\u0438\u0442\u0430\u043d\u0438\u044f. \u041f\u0435\u0440\u0432\u044b\u0439 \u0437\u0430\u043f\u0443\u0441\u043a unreal, \u0438 \u0441\u0440\u0430\u0437\u0443 \u043d\u0430\u0432\u0435\u044f\u043b\u043e \u043f\u0440\u0438\u044f\u0442\u043d\u044b\u0435 \u0432\u043e\u0441\u043f\u043e\u043c\u0438\u043d\u0430\u043d\u0438\u044f \u043e \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435 blender \u0434\u043e 2.8 \u0432\u0435\u0440\u0441\u0438\u0438, \u0438 \u0432 \u0433\u043e\u043b\u043e\u0432\u0435: \u043d\u0443 &#171;\u0433\u043e\u0439\u0434\u0430&#187;, \u0441\u043d\u043e\u0432\u0430 \u0432 \u043d\u0435\u0438\u0437\u0432\u0435\u0434\u0430\u043d\u043d\u044b\u0435 \u0434\u0430\u043b\u0438 <s>\u0441\u0442\u0440\u0430\u0434\u0430\u043d\u0438\u0439<\/s> \u0437\u043d\u0430\u043d\u0438\u0439. \u041e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0432\u0448\u0438\u0441\u044c \u0441 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u043e\u043c, \u043f\u0435\u0440\u0435\u0448\u0451\u043b \u043a \u043a\u0440\u0430\u0442\u043a\u043e\u043c\u0443 \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u043b\u0435\u043d\u0438\u044e \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438 C++. \u041d\u0430 \u0432\u0432\u043e\u0434\u043d\u0443\u044e \u0447\u0430\u0441\u0442\u044c \u0443\u0448\u043b\u043e \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0434\u043d\u0435\u0439, \u0432\u043e\u0437\u0440\u0430\u0441\u0442 \u0434\u0430\u0451\u0442 \u043e \u0441\u0435\u0431\u0435 \u0437\u043d\u0430\u0442\u044c, \u043c\u043e\u0437\u0433 \u043d\u0435 \u0442\u0430\u043a \u0433\u0438\u0431\u043e\u043a \u0438 \u0432\u0441\u044f\u0447\u0435\u0441\u043a\u0438 \u043f\u0440\u043e\u0442\u0438\u0432\u0438\u0442\u044c\u0441\u044f \u0432\u043e\u0441\u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0442\u044c \u043d\u043e\u0432\u0443\u044e \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e, \u0442\u0430\u043a \u0438 \u043d\u043e\u0440\u043e\u0432\u0438\u0442 \u0432\u044b\u0439\u0442\u0438 \u0438\u0437 \u043f\u043e\u0434 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044f \u0438 \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f \u043d\u0430 \u0447\u0442\u0435\u043d\u0438\u0435 \u0447\u0443\u0436\u0438\u0445 \u0441\u0442\u0430\u0442\u0435\u0439 <em>habr<\/em> \u0438 \u043d\u043e\u0432\u043e\u0441\u0442\u0435\u0439 \u0432 <em>3dnews<\/em>. \u041f\u0440\u043e\u0439\u0434\u044f \u043a\u0440\u0430\u0442\u043a\u0438\u0439 \u043a\u0443\u0440\u0441 \u043c\u043e\u043b\u043e\u0434\u043e\u0433\u043e \u0431\u043e\u0439\u0446\u0430, \u043f\u0440\u0438\u0441\u0442\u0443\u043f\u0430\u044e \u043a \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044e \u043f\u0435\u0440\u0432\u043e\u0439 \u0437\u0430\u0434\u0430\u0447\u0438.<\/p>\n<h2>1. \u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0444\u0430\u0439\u043b \u0444\u043e\u0442\u043e\u0433\u0440\u0430\u043c\u043c\u0435\u0442\u0440\u0438\u0438 \u043c\u0435\u0441\u0442\u043d\u043e\u0441\u0442\u0438 \u0432 unreal<\/h2>\n<figure class=\"float full-width\">\n<div><figcaption>\u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0432 Blender<\/figcaption><\/div>\n<\/figure>\n<p>\u0424\u043e\u0442\u043e\u0433\u0440\u0430\u043c\u043c\u0435\u0442\u0440\u0438\u044f \u043c\u043d\u0435 \u0432\u0441\u0442\u0440\u0435\u0447\u0430\u043b\u0430\u0441\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u0432 \u0441\u0442\u0430\u0442\u044c\u044f\u0445, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043d\u0438\u043a\u0430\u043a\u043e\u0433\u043e \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0441 \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u043c\u0438 \u0434\u0430\u043d\u043d\u044b\u043c\u0438 \u043d\u0435 \u0431\u044b\u043b\u043e, \u0442\u0435\u0441\u0442\u043e\u0432\u044b\u0439 \u0444\u0430\u0439\u043b \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0439 \u043e\u0442 \u043d\u043e\u0432\u044b\u0445 \u0442\u043e\u0432\u0430\u0440\u0438\u0449\u0435\u0439-\u0435\u0434\u0438\u043d\u043e\u043c\u044b\u0448\u043b\u0435\u043d\u043d\u0438\u043a\u043e\u0432 \u0431\u044b\u043b \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 \u043d\u0435\u043f\u043e\u043d\u044f\u0442\u043d\u043e\u043c unreal, <strong>.dae<\/strong>. \u0411\u0443\u0434\u0443\u0447\u0438 \u0430\u0432\u0442\u043e\u043d\u043e\u043c\u043d\u043e\u0439 \u0432\u044b\u0447\u0438\u0441\u043b\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u043e\u0439, \u0440\u0435\u0448\u0438\u043b \u0438\u0441\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0432\u043e\u0437\u043d\u0438\u043a\u0448\u0443\u044e \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443 \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e \u043f\u0435\u0440\u0435\u043a\u043e\u0434\u0438\u0440\u043e\u0432\u0430\u0432 \u0432 \u043d\u0443\u0436\u043d\u044b\u0439 \u0444\u043e\u0440\u043c\u0430\u0442. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0441\u044f \u0443\u0436\u0435 \u0434\u043e\u0440\u043e\u0433\u0438\u043c \u043c\u043d\u0435, \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u043d\u044b\u043c \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0435\u043d\u0438\u0435\u043c blender. Blender \u043e\u0442\u043a\u0440\u044b\u043b \u0444\u0430\u0439\u043b \u0431\u0435\u0437 \u043f\u0440\u043e\u0431\u043b\u0435\u043c, \u043a\u043e\u043d\u0432\u0435\u0440\u0442\u0438\u0440\u0443\u044e \u0434\u0430\u043d\u043d\u044b\u0435 \u0432 \u0444\u043e\u0440\u043c\u0430\u0442 .<strong>fbx<\/strong> \u0438 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u044e \u0432 unreal, \u043d\u0430\u0431\u043b\u044e\u0434\u0430\u044e \u043f\u043e\u0434\u043e\u0431\u043d\u0443\u044e \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443:<\/p>\n<figure class=\"bordered full-width\">\n<div><figcaption>\u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 &#8212; \u043f\u043e\u0442\u0435\u0440\u044f\u043d\u043d\u044b\u0435 \u0433\u0440\u0430\u043d\u0438<\/figcaption><\/div>\n<\/figure>\n<figure class=\"full-width\">\n<div><figcaption>\u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 &#8212; \u043f\u043e\u0442\u0435\u0440\u044f\u043d\u043d\u044b\u0435 \u0433\u0440\u0430\u043d\u0438<\/figcaption><\/div>\n<\/figure>\n<p>\u041d\u0435 \u044f\u0432\u043b\u044f\u044e\u0441\u044c \u044d\u043a\u0441\u043f\u0435\u0440\u0442\u043e\u043c \u0432 3d, \u043b\u044e\u0431\u0438\u0442\u0435\u043b\u044c, \u044d\u0442\u043e\u0442 \u0434\u0435\u0444\u0435\u043a\u0442 \u043c\u043d\u0435 \u0431\u044b\u043b \u043d\u0435 \u043f\u043e\u043d\u044f\u0442\u0435\u043d, \u0431\u043b\u0430\u0433\u043e \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442 \u0432\u0441\u0435\u0433\u0434\u0430 \u0440\u044f\u0434\u043e\u043c. \u0412\u043e\u043e\u0431\u0449\u0435 \u0442\u0440\u0443\u0434\u043d\u043e \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u043e\u0431\u0443\u0447\u0435\u043d\u0438\u0435 \u0442\u0430\u043a\u0438\u043c \u0432\u0435\u0449\u0430\u043c \u0431\u0435\u0437 \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442\u0430, \u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0431\u044b \u043f\u043e\u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043b\u043e\u0441\u044c \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0438 \u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043d\u0443\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0431\u044b \u043f\u043e\u0441\u0435\u0442\u0438\u0442\u044c \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a, \u0447\u0442\u043e \u0431\u044b \u0440\u0435\u0448\u0438\u0442\u044c \u043f\u043e\u0434\u043e\u0431\u043d\u0443\u044e \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443. \u0421\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439 \u0440\u0435\u0446\u0435\u043f\u0442 \u0440\u0435\u0448\u0435\u043d\u0438\u044f \u0438\u0437 \u043f\u0435\u0440\u0432\u044b\u0445 \u0434\u0432\u0443\u0445 \u0441\u0442\u0440\u0430\u043d\u0438\u0446 \u043f\u043e\u0438\u0441\u043a\u043e\u0432\u0438\u043a\u0430, \u043d\u0435 \u043f\u0440\u0438\u043d\u0435\u0441 \u0436\u0435\u043b\u0430\u0435\u043c\u043e\u0433\u043e \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442, \u043d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e\u0435 \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u0433\u0440\u0430\u043d\u0435\u0439. \u0421\u043a\u0443\u0434\u043d\u043e\u0441\u0442\u044c \u043c\u043e\u0435\u0433\u043e \u043a\u0443\u043b\u044c\u0442\u0443\u0440\u043d\u043e\u0433\u043e \u0441\u043b\u043e\u0432\u0430\u0440\u043d\u043e\u0433\u043e \u0437\u0430\u043f\u0430\u0441\u0430, \u043d\u0435 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043b\u0438\u0442\u0435\u0440\u0430\u0442\u0443\u0440\u043d\u043e \u043e\u043f\u0438\u0441\u0430\u0442\u044c \u043d\u0435\u0434\u043e\u0432\u043e\u043b\u044c\u0441\u0442\u0432\u043e. \u042f \u043d\u0430\u043f\u043e\u043b\u043d\u0438\u043b\u0441\u044f \u043d\u0435\u0433\u043e\u0434\u043e\u0432\u0430\u043d\u0438\u0435\u043c, \u043d\u043e \u0432\u0441\u0451 \u0436\u0435 \u0442\u0435\u0440\u043f\u0435\u043d\u0438\u0435 \u0438 \u0441\u043e\u0441\u0440\u0435\u0434\u043e\u0442\u043e\u0447\u0435\u043d\u043d\u043e\u0441\u0442\u044c \u043f\u0440\u0438\u0432\u0435\u043b\u0438 \u043c\u0435\u043d\u044f \u043a &#171;\u0434\u0435\u043b\u0430\u0435\u043c\u043e\u043c\u0443&#187;. \u0417\u0430 \u044d\u0442\u0438\u043c\u0438 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u043c\u0438 \u0441\u0442\u0440\u043e\u043a\u0430\u043c\u0438 \u0441\u043a\u0440\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043e\u0447\u0435\u043d\u044c \u043c\u043d\u043e\u0433\u043e \u043f\u0435\u0440\u0435\u0431\u043e\u0440\u0430, \u0445\u043e\u0440\u043e\u0448\u043e \u0447\u0442\u043e \u0434\u043e\u0434\u0443\u043c\u0430\u043b\u0441\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c, &#171;\u0431\u043e\u043b\u044c\u0448\u043e\u0439&#187; \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u0444\u0430\u0439\u043b \u044d\u0442\u043e \u0434\u043e\u043b\u0433\u0430\u044f \u043f\u0440\u043e\u0446\u0435\u0434\u0443\u0440\u0430, \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c blender&#8230; <\/p>\n<details class=\"spoiler\">\n<summary>\u041a\u043e\u0434, \u0440\u0435\u0448\u0438\u0432\u0448\u0438\u0439 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"python\">import bpy, bmesh from mathutils import Vector, Matrix import numpy as np   # \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043e\u0431\u044c\u0435\u043a\u0442 \u0438\u0437 \u0441\u0446\u0435\u043d\u044b bpy.context.active_object.select_set(False) foto_obj = bpy.context.scene.objects[0] bpy.context.view_layer.objects.active = foto_obj foto_obj.select_set(True)  # \u0412\u044b\u0431\u0440\u0430\u0442\u044c \u0432\u0441\u0435 \u0433\u0440\u0430\u043d\u0438 me = foto_obj.data bpy.ops.object.mode_set(mode=\"EDIT\") bpy.ops.mesh.select_mode(type = 'EDGE') bpy.ops.mesh.select_all(action = 'SELECT') \"\"\" \u0424\u0443\u043d\u043a\u0446\u0438\u044f Beautify Faces \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e  \u0441 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u043c\u0438 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u043c\u0438 \u0433\u0440\u0430\u043d\u044f\u043c\u0438.  <\/code><\/pre>\n<\/div>\n<\/details>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-355315","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/355315","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=355315"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/355315\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=355315"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=355315"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=355315"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}