/
FoksSerg
/
FSA-3dsMaxAutomation
Обзор
Документация
Войти
/
FoksSerg
/
FSA-3dsMaxAutomation
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/max_script_generator.py
283 строки
13 KB
Sergey Fokin
V1.2.0 - Переход на внутренние размеры комнат
29 окт 2025, 03:14
29 окт 2025, 03:14
6943419
Код
Авторство
О чём код?
#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ FSA-3dsMaxAutomation - Генератор MAXScript Модуль для генерации MAXScript кода для создания 3D модели в 3ds Max """ # Глобальные переменные проекта PROJECT_NAME = "FSA-3dsMaxAutomation" VERSION = "V1.1.0 (2025.10.29)" DEVELOPER = "@FoksSegr" from apartment_model import ApartmentModel, Room, Wall, OpeningType from typing import List import os class MaxScriptGenerator: """Генератор MAXScript для создания 3D модели квартиры в 3ds Max""" def __init__(self, apartment: ApartmentModel, scale_factor: float = 1.0): """ Инициализация генератора Args: apartment: Модель квартиры scale_factor: Масштабирование (1.0 = метры, 0.001 = миллиметры для 3ds Max) """ self.apartment = apartment self.scale_factor = scale_factor self.script_lines = [] self._object_counter = 0 def _add_line(self, line: str): """Добавить строку в скрипт""" self.script_lines.append(line) def _get_object_name(self, prefix: str = "Obj") -> str: """Получить уникальное имя объекта""" self._object_counter += 1 return f"{prefix}_{self._object_counter:03d}" def generate_script_header(self): """Генерация заголовка скрипта""" self._add_line("-- Generated by FSA-3dsMaxAutomation") self._add_line(f"-- Apartment: {self.apartment.name}") self._add_line(f"-- Version: {VERSION}") self._add_line("") self._add_line("-- Очистка сцены") self._add_line("delete $*") self._add_line("") def _convert_to_max_units(self, value: float) -> str: """Конвертировать метры в единицы 3ds Max""" # 3ds Max использует внутренние единицы, по умолчанию см # 1 метр = 100 см # Округляем до 2 знаков после запятой (0.01 см = 0.1 мм точность) result = round(value * 100 * self.scale_factor, 2) return str(result) def generate_box(self, name: str, x: float, y: float, z: float, length: float, width: float, height: float) -> str: """ Генерация команды создания Box в 3ds Max Args: name: Имя объекта x, y, z: Координаты нижнего угла length: Длина (по оси X) width: Ширина (по оси Y) height: Высота (по оси Z) """ length_str = self._convert_to_max_units(length) width_str = self._convert_to_max_units(width) height_str = self._convert_to_max_units(height) x_str = self._convert_to_max_units(x) y_str = self._convert_to_max_units(y) z_str = self._convert_to_max_units(z) script = f'{name} = box length:{length_str} width:{width_str} height:{height_str}' script += f' pos:[{x_str}, {y_str}, {z_str}]' self._add_line(script) return name def generate_room(self, room: Room): """Генерация комнаты (пол + потолок + стены) ВАЖНО: room.length и room.width теперь ВНУТРЕННИЕ размеры (без стен) """ self._add_line(f"-- Комната: {room.name}") # Используем толщину стен из модели комнаты wall_thickness = room.wall_thickness # ВНУТРЕННИЕ размеры = room.length, room.width # ВНЕШНИЕ размеры = room.length + 2*wall_thickness, room.width + 2*wall_thickness # Пол (внутренняя площадь, без стен) floor_name = self._get_object_name("Floor") floor_x = room.x + wall_thickness floor_y = room.y + wall_thickness floor_length = room.length # Уже внутренний размер floor_width = room.width # Уже внутренний размер self.generate_box(floor_name, floor_x, floor_y, 0, floor_length, floor_width, 0.05) # Толщина пола 5см self._add_line(f'{floor_name}.name = "Пол_{room.name}"') self._add_line("") # Потолок (внутренняя площадь, без стен) ceiling_name = self._get_object_name("Ceiling") self.generate_box(ceiling_name, floor_x, floor_y, room.height, floor_length, floor_width, 0.05) # Толщина потолка 5см self._add_line(f'{ceiling_name}.name = "Потолок_{room.name}"') self._add_line("") # Проверяем видимость стен из модели walls_visible = getattr(room, 'walls_visible', { "top": True, "bottom": True, "left": True, "right": True }) # Внешние размеры для расчёта позиций стен outer_length = room.length + 2 * wall_thickness outer_width = room.width + 2 * wall_thickness # Стена 1 (нижняя, bottom) - только если видима if walls_visible.get("bottom", True): self._add_line(f"-- Стена (нижняя)") wall1_name = self._get_object_name("Wall") self.generate_box(wall1_name, room.x, room.y, 0, outer_length, wall_thickness, room.height) self._add_line(f'{wall1_name}.name = "Стена_Нижняя_{room.name}"') self._add_line("") # Стена 2 (верхняя, top) - только если видима if walls_visible.get("top", True): self._add_line(f"-- Стена (верхняя)") wall2_name = self._get_object_name("Wall") self.generate_box(wall2_name, room.x, room.y + outer_width - wall_thickness, 0, outer_length, wall_thickness, room.height) self._add_line(f'{wall2_name}.name = "Стена_Верхняя_{room.name}"') self._add_line("") # Стена 3 (левая, left) - только если видима if walls_visible.get("left", True): self._add_line(f"-- Стена (левая)") wall3_name = self._get_object_name("Wall") self.generate_box(wall3_name, room.x, room.y, 0, wall_thickness, outer_width, room.height) self._add_line(f'{wall3_name}.name = "Стена_Левая_{room.name}"') self._add_line("") # Стена 4 (правая, right) - только если видима if walls_visible.get("right", True): self._add_line(f"-- Стена (правая)") wall4_name = self._get_object_name("Wall") self.generate_box(wall4_name, room.x + outer_length - wall_thickness, room.y, 0, wall_thickness, outer_width, room.height) self._add_line(f'{wall4_name}.name = "Стена_Правая_{room.name}"') self._add_line("") def generate_doors_and_windows(self, room: Room): """Генерация дверей и окон для комнаты""" self._add_line(f"-- Двери и окна для {room.name}") # Генерация окон for idx, window in enumerate(room.windows): window_name = self._get_object_name("Window") self._add_line(f"-- Окно {idx + 1}: {window.name}") # Окно создается как Box в стене # TODO: Реализовать логику размещения окон self._add_line(f'{window_name} = box name:"{window.name}"') self._add_line("") # Генерация дверей for idx, door in enumerate(room.doors): door_name = self._get_object_name("Door") self._add_line(f"-- Дверь {idx + 1}: {door.name}") # Дверь создается как Box в стене # TODO: Реализовать логику размещения дверей self._add_line(f'{door_name} = box name:"{door.name}"') self._add_line("") def generate_full_apartment(self): """Генерация полной модели квартиры""" self.generate_script_header() self._add_line("-- Создание комнат") self._add_line("") for room in self.apartment.rooms: self.generate_room(room) self.generate_doors_and_windows(room) # Генерация внешних стен if self.apartment.walls: self._add_line("-- Внешние стены") self._add_line("") for wall in self.apartment.walls: self.generate_wall(wall) # Добавление материалов self._add_line("-- Материалы") self._add_line("") self.generate_materials() self._add_line("-- Скрипт успешно выполнен") self._add_line(f"-- Квартира '{self.apartment.name}' создана") self._add_line(f"-- Площадь: {self.apartment.get_total_area():.2f} кв.м") self._add_line(f"-- Комнат: {self.apartment.get_rooms_count()}") def generate_wall(self, wall: Wall): """Генерация отдельной стены""" wall_name = self._get_object_name("Wall") self._add_line(f"-- Стена: {wall.name}") # Расчет размеров и положения стены dx = wall.x2 - wall.x1 dy = wall.y2 - wall.y1 length = (dx ** 2 + dy ** 2) ** 0.5 # Центр стены center_x = (wall.x1 + wall.x2) / 2 center_y = (wall.y1 + wall.y2) / 2 self.generate_box(wall_name, center_x - length / 2, center_y - wall.thickness / 2, 0, length, wall.thickness, wall.height) self._add_line(f'{wall_name}.name = "{wall.name}"') self._add_line("") def generate_materials(self): """Генерация материалов""" self._add_line("-- Создание материала для стен") self._add_line('mat_wall = Standardmaterial name:"Материал_Стены"') self._add_line('mat_wall.diffuse = color 255 255 255') self._add_line("") self._add_line("-- Создание материала для пола") self._add_line('mat_floor = Standardmaterial name:"Материал_Пол"') self._add_line('mat_floor.diffuse = color 230 230 230') self._add_line("") self._add_line("-- Применение материалов") self._add_line('for obj in objects where matchPattern obj.name pattern:"*Стена*" do obj.material = mat_wall') self._add_line('for obj in objects where matchPattern obj.name pattern:"Пол_*" do obj.material = mat_floor') self._add_line('for obj in objects where matchPattern obj.name pattern:"Потолок_*" do obj.material = mat_floor') self._add_line("") def get_script(self) -> str: """Получить сгенерированный скрипт""" return "\n".join(self.script_lines) def save_to_file(self, filepath: str): """Сохранить скрипт в файл""" with open(filepath, 'w', encoding='utf-8') as f: f.write(self.get_script()) def save_to_max_file(self, filepath: str): """ Сохранить сцену как MAX файл Args: filepath: Путь к файлу .max """ # В 3ds Max для сохранения используется команда saveMaxFile self._add_line(f'-- Сохранение файла') self._add_line(f'saveMaxFile @"{filepath}"') self._add_line("") # Перезаписываем файл с командами сохранения with open(filepath, 'w', encoding='utf-8') as f: for line in self.script_lines[:-1]: # Исключаем последнюю строку saveMaxFile if 'saveMaxFile' not in line: f.write(line + '\n') # Добавляем saveMaxFile в конец f.write(f'saveMaxFile @"{filepath}"\n')