Skip to content

feat(stage-ui): VRM model setting page: FOV and Rotation#307

Merged
nekomeowww merged 19 commits intomoeru-ai:mainfrom
Lilia-Chen:lilia_work
Jul 21, 2025
Merged

feat(stage-ui): VRM model setting page: FOV and Rotation#307
nekomeowww merged 19 commits intomoeru-ai:mainfrom
Lilia-Chen:lilia_work

Conversation

@Lilia-Chen
Copy link
Contributor

Description

New features for the VRM model setting page:

  1. Adjust FOV
  2. Adjust model rotation

Linked Issues

Trackers for some VRM model future ideas

Additional context

Due to the functionality limits of Tres.js and Three.js, there is no way to use them to implement a similar camera movement trace as it is in Blender. The focusing target is also the rotation centre of the camera movement.

Therefore, I just banned the pan function in the OrbitControls. Currently, users can only rotate and "scale" (actually changing the distance between the target and the camera) in the interactive TresCanvas.

@netlify
Copy link

netlify bot commented Jul 20, 2025

Deploy Preview for airi-vtuber ready!

Name Link
🔨 Latest commit 9af4e30
🔍 Latest deploy log https://app.netlify.com/projects/airi-vtuber/deploys/687e1f27b4449f00082ce18a
😎 Deploy Preview https://deploy-preview-307--airi-vtuber.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify
Copy link

netlify bot commented Jul 20, 2025

Deploy Preview for airi-docs ready!

Name Link
🔨 Latest commit 9af4e30
🔍 Latest deploy log https://app.netlify.com/projects/airi-docs/deploys/687e1f270f91cb00081915b3
😎 Deploy Preview https://deploy-preview-307--airi-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary of Changes

Hello @Lilia-Chen, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the VRM model setting page by introducing new user controls for camera Field of View (FOV) and model Y-axis rotation. It refines the interactive experience by implementing a custom OrbitControls that restricts camera movement to rotation and zooming, aligning with the technical limitations and desired user interaction. Furthermore, the changes include improvements to model loading logic, dynamic adjustment of positioning controls, and persistence of user settings, all contributing to a more flexible and user-friendly VRM model customization experience.

Highlights

  • VRM Model Customization: Introduced new UI controls for adjusting the camera's Field of View (FOV) and the VRM model's rotation around the Y-axis, enhancing user control over model presentation.
  • Improved Camera Interaction: Implemented a custom OrbitControls component that explicitly disables camera panning, ensuring that users can only rotate and 'zoom' (dolly) the VRM model, aligning with the stated design constraints.
  • Dynamic Model Positioning: Refined the X, Y, and Z offset sliders to dynamically adjust their ranges based on the loaded VRM model's dimensions, providing more intuitive and relevant controls.
  • Enhanced Model Loading & Persistence: Updated the model loading mechanism to prevent redundant reloads and introduced localStorage persistence for camera FOV, initial camera position, and model Y-rotation settings.
  • Structural Improvements: Encapsulated the VRM model within a new VRMGroup object, simplifying model manipulation (like rotation) and enabling a more robust initial camera positioning calculation based on model size.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in issue comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist is currently in preview and may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments to provide feedback.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@Lilia-Chen Lilia-Chen changed the title Lilia work feat(stage-ui): VRM model setting page: FOV and Rotation Jul 20, 2025
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces new features for the VRM model settings page, including camera Field of View (FOV) adjustment and model rotation. It also includes a refactoring of the VRM scene setup. I've identified a critical bug in the scene graph manipulation that would break model rotation, and a high-severity issue where the camera's aspect ratio doesn't update on resize, causing visual distortion.

Comment on lines +49 to +54
// watch([width, height], () => {
// if (camera.value) {
// camera.value.aspect = width.value / height.value
// camera.value.updateProjectionMatrix()
// }
// })
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The commented-out watcher for width and height is necessary for keeping the camera's aspect ratio in sync with the component's dimensions. Without it, the rendered scene will appear stretched or squashed if the component or window is resized. Enable this to prevent visual distortions.

watch([width, height], () => {
  if (camera.value) {
    camera.value.aspect = width.value / height.value
    camera.value.updateProjectionMatrix()
  }
})

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tbh I'm not sure if this is necessary or ideal. Although this can let the camera's aspect ratio be in sync with the component's dimensions... due to the render of the animation, the image will be stretched... which is ugly tbf.

Comment on lines +52 to +69
function urlUploadClick() {
modelUrl.value = localModelUrl.value
// same URL will let the loader be lazy and forgot to reset loading state
// If the loading state is still true, then the URL input will be locked
if (modelUrl.value === selectedModel.value) {
console.warn('Model URL is the same as the selected model, no need to reload.')
return
}
// Can't let the default model URL be reentered into the loader, otherwise it will still be too lazy to reset the loading state
if (!modelUrl.value && selectedModel.value === defaultModelUrl.value) {
localModelUrl.value = defaultModelUrl.value
return
}
// Only when real different URL is entered, then the loader will be triggered
loadSource.value = 'url'
loadingModel.value = true
localModelUrl.value = selectedModel.value
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The line localModelUrl.value = selectedModel.value at the end of urlUploadClick function will reset the URL input field to the previously loaded model's URL right after the user clicks the load button. This can be confusing for the user, as the URL they just entered will be replaced. Removing this line preserves the user's input in the text field.

function urlUploadClick() {
  modelUrl.value = localModelUrl.value
  // same URL will let the loader be lazy and forgot to reset loading state
  // If the loading state is still true, then the URL input will be locked
  if (modelUrl.value === selectedModel.value) {
    console.warn('Model URL is the same as the selected model, no need to reload.')
    return
  }
  // Can't let the default model URL be reentered into the loader, otherwise it will still be too lazy to reset the loading state
  if (!modelUrl.value && selectedModel.value === defaultModelUrl.value) {
    localModelUrl.value = defaultModelUrl.value
    return
  }
  // Only when real different URL is entered, then the loader will be triggered
  loadSource.value = 'url'
  loadingModel.value = true
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This may not be the most elegant way to do things, but I just want to consider the following situation:

  1. In common, we hope the input area retains what the user has input
  2. If the user inputs nothing and clicks the import form URL button, then we just load the default model
  3. The default model still has a URL, so I think maybe it's better to put it in the input area as a reminder that there is a URL for the model

@nekomeowww What's your opinion on this? Just want to seek some advice on the front-end design~


// Compute the initial camera position (once per loaded model)
// In order to see the up-2/3 part fo the model, z = (y/3) / tan(fov/2)
const fov = 40 // default fov = 40 degrees
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Learned, thanks!

Copy link
Member

@nekomeowww nekomeowww left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Much appreciated! 🎉 You made a lot progress for our 3D scenes for VRM models. Really thanks. One of the best contributors in field of 3D.

@nekomeowww nekomeowww merged commit a8454b4 into moeru-ai:main Jul 21, 2025
14 checks passed
@Lilia-Chen Lilia-Chen deleted the lilia_work branch July 21, 2025 12:13
baysonfox pushed a commit to baysonfox/airi that referenced this pull request Jul 24, 2025
Disqort pushed a commit to Disqort/airi that referenced this pull request Aug 29, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants