daydev-patch-1 #105443
23
aframe_exporter/LICENSE
Normal file
23
aframe_exporter/LICENSE
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2024 Banyapon Poolsawas associated with
|
||||||
|
College of Creative Design and Entertainment Technology,
|
||||||
|
Dhurakij Pundit University and Daydev Co., Ltd.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
52
aframe_exporter/README.md
Normal file
52
aframe_exporter/README.md
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
# Blender A-Frame Exporter
|
||||||
|
|
||||||
|
[![Blender Version](https://img.shields.io/badge/Blender-3.0+-orange.svg)](https://www.blender.org/) [![Blender Version](https://img.shields.io/badge/Blender-4.0+-orange.svg)](https://www.blender.org/)
|
||||||
|
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
This Blender add-on simplifies the process of exporting your 3D scenes into interactive web experiences using A-Frame, a popular web framework for building virtual reality (VR) and augmented reality (AR) experiences.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
![Showcase](https://github.com/banyapon/Blender-Aframe-Exporter/blob/main/dist/8.png?raw=true)
|
||||||
|
|
||||||
|
* **Seamless Export:** Quickly export your entire Blender scene, including models, materials, and animations, to A-Frame HTML and GLB (binary glTF) files.
|
||||||
|
* **Collision Detection:** Automatically add collision detection to your exported models using A-Frame's physics system, enabling interactive experiences.
|
||||||
|
* **Camera Controls:** Easily set up basic WASD controls for navigating your A-Frame scene with the camera.
|
||||||
|
* **Customization:** Customize the generated A-Frame code to add additional features, components, or interactions.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
1. Download the latest release of the add-on (`.zip` file) from the [Releases](https://github.com/banyapon/Blender-Aframe-Exporter/releases/tag/release) page.
|
||||||
|
2. In Blender, go to `Edit` > `Preferences` > `Add-ons` > `Install...`
|
||||||
|
![Go to `Edit` > `Preferences` > `Add-ons` > `Install](https://raw.githubusercontent.com/banyapon/Blender-Aframe-Exporter/main/dist/1.png)
|
||||||
|
3. Select the downloaded `.zip` file and click `Install Add-on`.
|
||||||
|
![Zip File](https://github.com/banyapon/Blender-Aframe-Exporter/blob/main/dist/2.png?raw=true)
|
||||||
|
4. Enable the "A-Frame Exporter" add-on in the list.
|
||||||
|
![Enable add-on](https://github.com/banyapon/Blender-Aframe-Exporter/blob/main/dist/3.png?raw=true)
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
1. Create your 3D scene in Blender.
|
||||||
|
![Create Scene](https://github.com/banyapon/Blender-Aframe-Exporter/blob/main/dist/5.png?raw=true)
|
||||||
|
2. Go to `File` > `Export` > `A-Frame (.html)`.
|
||||||
|
|
||||||
|
![Export Aframe](https://github.com/banyapon/Blender-Aframe-Exporter/blob/main/dist/6.png?raw=true)
|
||||||
|
|
||||||
|
3. Choose a location to save the files and click `Export A-Frame`.
|
||||||
|
![VS Code](https://github.com/banyapon/Blender-Aframe-Exporter/blob/main/dist/7.png?raw=true)
|
||||||
|
4. The add-on will generate an HTML file (`your_scene_name.html`) and a GLB file (`your_scene_name.glb`).
|
||||||
|
5. Open the HTML file in a web browser to view your interactive A-Frame scene.
|
||||||
|
![WebXR](https://github.com/banyapon/Blender-Aframe-Exporter/blob/main/dist/9.png?raw=true)
|
||||||
|
|
||||||
|
## Additional Notes
|
||||||
|
|
||||||
|
* Make sure you have a web server running to view the exported HTML file correctly.
|
||||||
|
* For more advanced A-Frame features and customization, refer to the [A-Frame documentation](https://aframe.io/docs/).
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
Contributions are welcome! Feel free to submit issues, feature requests, or pull requests.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This add-on is released under the MIT License.
|
27
aframe_exporter/__init__.py
Normal file
27
aframe_exporter/__init__.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import bpy
|
||||||
|
from . import aframe_exporter
|
||||||
|
|
||||||
|
bl_info = {
|
||||||
|
"name": "A-Frame Exporter",
|
||||||
|
"author": "Banyapon Poolsawas",
|
||||||
|
"version": (1, 0, 2),
|
||||||
|
"blender": (4, 0, 1),
|
||||||
|
"location": "File > Export",
|
||||||
|
"description": "Export Blender scene to A-Frame HTML and GLB for WebXR",
|
||||||
|
"warning": "",
|
||||||
|
"category": "Export",
|
||||||
|
}
|
||||||
|
|
||||||
|
def menu_func_export(self, context):
|
||||||
|
self.layout.operator(aframe_exporter.ExportAFrame.bl_idname, text="A-Frame (.html)")
|
||||||
|
|
||||||
|
def register():
|
||||||
|
bpy.utils.register_class(aframe_exporter.ExportAFrame)
|
||||||
|
bpy.types.TOPBAR_MT_file_export.append(menu_func_export)
|
||||||
|
|
||||||
|
def unregister():
|
||||||
|
bpy.utils.unregister_class(aframe_exporter.ExportAFrame)
|
||||||
|
bpy.types.TOPBAR_MT_file_export.remove(menu_func_export)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
register()
|
143
aframe_exporter/aframe_exporter.py
Normal file
143
aframe_exporter/aframe_exporter.py
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
import bpy
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import http.server
|
||||||
|
import socketserver
|
||||||
|
import threading
|
||||||
|
import webbrowser
|
||||||
|
from bpy.props import BoolProperty, IntProperty
|
||||||
|
from bpy_extras.io_utils import ExportHelper
|
||||||
|
|
||||||
|
class ServerThread(threading.Thread):
|
||||||
|
def __init__(self, directory, port):
|
||||||
|
super().__init__()
|
||||||
|
self.directory = directory
|
||||||
|
self.port = port
|
||||||
|
self.httpd = None
|
||||||
|
self.error = None
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
os.chdir(self.directory)
|
||||||
|
|
||||||
|
# Error handling for the server
|
||||||
|
try:
|
||||||
|
handler = http.server.SimpleHTTPRequestHandler
|
||||||
|
with socketserver.TCPServer(("", self.port), handler) as httpd:
|
||||||
|
self.httpd = httpd
|
||||||
|
print(f"Serving at port {self.port}")
|
||||||
|
httpd.serve_forever()
|
||||||
|
except OSError as e:
|
||||||
|
self.error = e
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
if self.httpd:
|
||||||
|
self.httpd.shutdown()
|
||||||
|
|
||||||
|
class ExportAFrame(bpy.types.Operator, ExportHelper):
|
||||||
|
bl_idname = "export_scene.aframe"
|
||||||
|
bl_label = "Export A-Frame"
|
||||||
|
filename_ext = ".html"
|
||||||
|
|
||||||
|
start_server: BoolProperty(
|
||||||
|
name="Start Local Server",
|
||||||
|
description="Automatically start a local server after export",
|
||||||
|
default=False
|
||||||
|
)
|
||||||
|
|
||||||
|
port: IntProperty(
|
||||||
|
name="Port",
|
||||||
|
description="Port number for the local server",
|
||||||
|
default=8200,
|
||||||
|
min=1,
|
||||||
|
max=65535
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
filepath = self.filepath
|
||||||
|
glb_filepath = os.path.splitext(filepath)[0] + ".glb"
|
||||||
|
|
||||||
|
# Export GLB
|
||||||
|
bpy.ops.export_scene.gltf(filepath=glb_filepath, export_format='GLB')
|
||||||
|
|
||||||
|
# Copy favicon
|
||||||
|
favicon_path = os.path.join(os.path.dirname(__file__), "icons", "favicon.ico")
|
||||||
|
if os.path.exists(favicon_path):
|
||||||
|
shutil.copy(favicon_path, os.path.dirname(filepath))
|
||||||
|
|
||||||
|
# Create HTML (A-Frame)
|
||||||
|
with open(filepath, "w") as f:
|
||||||
|
f.write(f"""
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link rel="icon" href="favicon.ico">
|
||||||
|
<title>Aframe Exporter</title>
|
||||||
|
<script src="https://aframe.io/releases/1.6.0/aframe.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/gh/c-frame/aframe-extras@7.5.1/dist/aframe-extras.min.js"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<a-scene>
|
||||||
|
<a-assets>
|
||||||
|
<a-asset-item id="model" src="{os.path.basename(glb_filepath)}"></a-asset-item>
|
||||||
|
</a-assets>
|
||||||
|
|
||||||
|
<a-entity id="cameraRig" movement-controls="fly: false; speed: 0.2;">
|
||||||
|
<a-entity id="camera" camera look-controls position="0 1.6 0"></a-entity>
|
||||||
|
<a-entity oculus-touch-controls="hand: left"></a-entity>
|
||||||
|
<a-entity oculus-touch-controls="hand: right"></a-entity>
|
||||||
|
</a-entity>
|
||||||
|
|
||||||
|
<a-entity gltf-model="#model" static-body></a-entity>
|
||||||
|
</a-scene>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
""")
|
||||||
|
|
||||||
|
if self.start_server:
|
||||||
|
self.server_thread = ServerThread(os.path.dirname(filepath), self.port)
|
||||||
|
self.server_thread.start()
|
||||||
|
|
||||||
|
if self.server_thread.error: # Check for errors
|
||||||
|
self.report({'ERROR'}, f"Failed to start server: {self.server_thread.error}")
|
||||||
|
return {'CANCELLED'}
|
||||||
|
|
||||||
|
url = f"http://localhost:{self.port}/{os.path.basename(filepath)}"
|
||||||
|
else:
|
||||||
|
url = "file://" + filepath
|
||||||
|
|
||||||
|
webbrowser.open_new_tab(url)
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
def start_local_server(self, directory, port):
|
||||||
|
os.chdir(directory)
|
||||||
|
handler = http.server.SimpleHTTPRequestHandler
|
||||||
|
with socketserver.TCPServer(("", port), handler) as httpd:
|
||||||
|
print(f"Serving at port {port}")
|
||||||
|
threading.Thread(target=httpd.serve_forever).start()
|
||||||
|
|
||||||
|
def invoke(self, context, event):
|
||||||
|
context.window_manager.fileselect_add(self)
|
||||||
|
return {'RUNNING_MODAL'}
|
||||||
|
|
||||||
|
class ExportAFramePanel(bpy.types.Panel):
|
||||||
|
bl_label = "A-Frame Export Settings" # Label for the panel
|
||||||
|
bl_idname = "OBJECT_PT_aframe_export" # Unique identifier for the panel
|
||||||
|
bl_space_type = 'PROPERTIES' # Panel location (Properties Editor)
|
||||||
|
bl_region_type = 'WINDOW' # Panel type (window)
|
||||||
|
bl_context = "scene" # Context for the panel (Scene)
|
||||||
|
bl_options = {'DEFAULT_CLOSED'} # Panel is closed by default
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
|
||||||
|
# Export Button
|
||||||
|
layout.operator("export_scene.aframe", text="Export A-Frame") # Add the export operator button
|
||||||
|
|
||||||
|
# Local Server Options
|
||||||
|
row = layout.row() # Create a new row for the checkbox and button
|
||||||
|
row.prop(context.scene, "aframe_local_server", text="Start Local Server") # Checkbox to toggle local server
|
||||||
|
if context.scene.aframe_local_server: # Show port input if the checkbox is checked
|
||||||
|
row = layout.row() # Create a new row for the port input
|
||||||
|
row.prop(context.scene, "aframe_server_port", text="Port") # Input field for the port number
|
BIN
aframe_exporter/icons/favicon.ico
Normal file
BIN
aframe_exporter/icons/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
Loading…
Reference in New Issue
Block a user