Merge branch 'master' into FIRE-35686
commit
fb06fd9506
|
|
@ -218,8 +218,10 @@ jobs:
|
|||
prefix=${ba[0]}
|
||||
if [ "$prefix" == "project" ]; then
|
||||
IFS='_' read -ra prj <<< "${ba[1]}"
|
||||
prj_str="${prj[*]}"
|
||||
# uppercase first letter of each word
|
||||
export viewer_channel="Second Life Project ${prj[*]^}"
|
||||
capitalized=$(echo "$prj_str" | awk '{for (i=1; i<=NF; i++) $i = toupper(substr($i,1,1)) substr($i,2); print}')
|
||||
export viewer_channel="Second Life Project $capitalized"
|
||||
elif [[ "$prefix" == "release" || "$prefix" == "main" ]];
|
||||
then
|
||||
export viewer_channel="Second Life Release"
|
||||
|
|
@ -304,7 +306,7 @@ jobs:
|
|||
AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
|
||||
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
|
||||
needs: build
|
||||
runs-on: windows-large
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Sign and package Windows viewer
|
||||
if: env.AZURE_KEY_VAULT_URI && env.AZURE_CERT_NAME && env.AZURE_CLIENT_ID && env.AZURE_CLIENT_SECRET && env.AZURE_TENANT_ID
|
||||
|
|
@ -455,7 +457,6 @@ jobs:
|
|||
prerelease: true
|
||||
generate_release_notes: true
|
||||
target_commitish: ${{ github.sha }}
|
||||
previous_tag: release
|
||||
append_body: true
|
||||
fail_on_unmatched_files: true
|
||||
files: |
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ jobs:
|
|||
build_matrix:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macos-14,ubuntu-22.04,windows-2022]
|
||||
os: [macos-15,ubuntu-24.04,windows-2022]
|
||||
grid: [sl,os]
|
||||
variant: [regular, avx]
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
|
@ -214,7 +214,7 @@ jobs:
|
|||
- name: Install required Ubuntu packages and release some space.
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
dependencies=("python3-setuptools" "mesa-common-dev" "libgl1-mesa-dev" "libxinerama-dev" "libxrandr-dev" "libpulse-dev" "libglu1-mesa-dev" "libfreetype6-dev" "libfontconfig1")
|
||||
dependencies=("python3-setuptools" "mesa-common-dev" "libgl1-mesa-dev" "libxinerama-dev" "libxrandr-dev" "libpulse-dev" "libglu1-mesa-dev" "libfreetype6-dev" "libfontconfig1-dev")
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y "${dependencies[@]}"
|
||||
sudo apt-get install -y wget apt-transport-https software-properties-common
|
||||
|
|
@ -283,10 +283,10 @@ jobs:
|
|||
EXPIRE_DAYS=14
|
||||
;;
|
||||
"Alpha")
|
||||
EXPIRE_DAYS=28
|
||||
EXPIRE_DAYS=14
|
||||
;;
|
||||
"Beta")
|
||||
EXPIRE_DAYS=60
|
||||
EXPIRE_DAYS=28
|
||||
;;
|
||||
*)
|
||||
EXPIRE_DAYS=""
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
name: Check PR
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, edited, reopened, synchronize]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
check-description:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check PR description
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const description = context.payload.pull_request.body || '';
|
||||
if (description.trim().length < 20) {
|
||||
core.setFailed("❌ PR description is too short. Please provide at least 20 characters.");
|
||||
}
|
||||
|
|
@ -1,70 +1,199 @@
|
|||
name: Run QA Test # Runs automated tests on a self-hosted QA machine
|
||||
permissions:
|
||||
contents: read
|
||||
#pull-requests: write # maybe need to re-add this later
|
||||
name: Run QA Test # Runs automated tests on self-hosted QA machines
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ["Build"]
|
||||
types:
|
||||
- completed
|
||||
|
||||
concurrency:
|
||||
group: qa-test-run
|
||||
cancel-in-progress: true # Cancels any queued job when a new one starts
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
build_id:
|
||||
description: 'Build workflow run ID (e.g. For github.com/secondlife/viewer/actions/runs/1234567890 the ID is 1234567890)'
|
||||
required: true
|
||||
default: '14806728332'
|
||||
|
||||
jobs:
|
||||
debug-workflow:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Debug Workflow Variables
|
||||
env:
|
||||
HEAD_BRANCH: ${{ github.event.workflow_run.head_branch }}
|
||||
HEAD_COMMIT_MSG: ${{ github.event.workflow_run.head_commit.message }}
|
||||
run: |
|
||||
echo "Workflow Conclusion: ${{ github.event.workflow_run.conclusion }}"
|
||||
echo "Workflow Head Branch: $HEAD_BRANCH"
|
||||
echo "Workflow Head Branch: ${{ github.event.workflow_run.head_branch }}"
|
||||
echo "Workflow Run ID: ${{ github.event.workflow_run.id }}"
|
||||
echo "Head Commit Message: $HEAD_COMMIT_MSG"
|
||||
echo "Head Commit Message: ${{ github.event.workflow_run.head_commit.message }}"
|
||||
echo "GitHub Ref: ${{ github.ref }}"
|
||||
echo "GitHub Ref Name: ${{ github.ref_name }}"
|
||||
echo "GitHub Event Name: ${{ github.event_name }}"
|
||||
echo "GitHub Workflow Name: ${{ github.workflow }}"
|
||||
|
||||
install-viewer-and-run-tests:
|
||||
runs-on: [self-hosted, qa-machine]
|
||||
# Run test only on successful builds of Second_Life_X branches
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ matrix.runner }}
|
||||
cancel-in-progress: false # Prevents cancellation of in-progress jobs
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- os: windows
|
||||
runner: qa-windows-atlas
|
||||
artifact: Windows-installer
|
||||
install-path: 'C:\viewer-automation-main'
|
||||
- os: windows
|
||||
runner: qa-dan-asus
|
||||
artifact: Windows-installer
|
||||
install-path: 'C:\viewer-automation-main'
|
||||
# Commented out until mac runner is available
|
||||
# - os: mac
|
||||
# runner: qa-mac-atlas
|
||||
# artifact: Mac-installer
|
||||
# install-path: '$HOME/Documents/viewer-automation'
|
||||
fail-fast: false
|
||||
|
||||
runs-on: [self-hosted, "${{ matrix.runner }}"]
|
||||
# Run test only on successful builds of Second_Life_X branches or on manual dispatch
|
||||
if: >
|
||||
(github.event_name == 'workflow_run' &&
|
||||
github.event.workflow_run.conclusion == 'success' &&
|
||||
(
|
||||
startsWith(github.event.workflow_run.head_branch, 'Second_Life')
|
||||
)
|
||||
startsWith(github.event.workflow_run.head_branch, 'Second_Life')) ||
|
||||
github.event_name == 'workflow_dispatch'
|
||||
|
||||
steps:
|
||||
- name: Temporarily Allow PowerShell Scripts (Process Scope)
|
||||
# Windows-specific steps
|
||||
- name: Set Build ID
|
||||
if: matrix.os == 'windows'
|
||||
shell: pwsh
|
||||
run: |
|
||||
if ("${{ github.event_name }}" -eq "workflow_dispatch") {
|
||||
echo "BUILD_ID=${{ github.event.inputs.build_id }}" | Out-File -FilePath $env:GITHUB_ENV -Append
|
||||
echo "ARTIFACTS_URL=https://api.github.com/repos/secondlife/viewer/actions/runs/${{ github.event.inputs.build_id }}/artifacts" | Out-File -FilePath $env:GITHUB_ENV -Append
|
||||
} else {
|
||||
echo "BUILD_ID=${{ github.event.workflow_run.id }}" | Out-File -FilePath $env:GITHUB_ENV -Append
|
||||
echo "ARTIFACTS_URL=https://api.github.com/repos/secondlife/viewer/actions/runs/${{ github.event.workflow_run.id }}/artifacts" | Out-File -FilePath $env:GITHUB_ENV -Append
|
||||
}
|
||||
|
||||
- name: Temporarily Allow PowerShell Scripts (Windows)
|
||||
if: matrix.os == 'windows'
|
||||
shell: pwsh
|
||||
run: |
|
||||
Set-ExecutionPolicy RemoteSigned -Scope Process -Force
|
||||
|
||||
- name: Verify viewer-sikulix-main Exists
|
||||
run: |
|
||||
if (-Not (Test-Path -Path 'C:\viewer-sikulix-main')) {
|
||||
Write-Host '❌ Error: viewer-sikulix not found on runner!'
|
||||
exit 1
|
||||
}
|
||||
Write-Host '✅ viewer-sikulix is already available.'
|
||||
|
||||
- name: Fetch & Download Windows Installer Artifact
|
||||
- name: Verify viewer-automation-main Exists (Windows)
|
||||
if: matrix.os == 'windows'
|
||||
shell: pwsh
|
||||
run: |
|
||||
$BUILD_ID = "${{ github.event.workflow_run.id }}"
|
||||
$ARTIFACTS_URL = "https://api.github.com/repos/secondlife/viewer/actions/runs/$BUILD_ID/artifacts"
|
||||
if (-Not (Test-Path -Path '${{ matrix.install-path }}')) {
|
||||
Write-Host '❌ Error: viewer-automation folder not found on runner!'
|
||||
exit 1
|
||||
}
|
||||
Write-Host '✅ viewer-automation folder is provided.'
|
||||
|
||||
- name: Verify viewer-automation-main is Up-To-Date (Windows)
|
||||
if: matrix.os == 'windows'
|
||||
shell: pwsh
|
||||
continue-on-error: true
|
||||
run: |
|
||||
cd ${{ matrix.install-path }}
|
||||
Write-Host "Checking for repository updates..."
|
||||
|
||||
# Check if .git directory exists
|
||||
if (Test-Path -Path ".git") {
|
||||
try {
|
||||
# Save local changes instead of discarding them
|
||||
git stash push -m "Automated stash before update $(Get-Date)"
|
||||
Write-Host "Local changes saved (if any)"
|
||||
|
||||
# Update the repository
|
||||
git pull
|
||||
Write-Host "✅ Repository updated successfully"
|
||||
|
||||
# Try to restore local changes if any were stashed
|
||||
$stashList = git stash list
|
||||
if ($stashList -match "Automated stash before update") {
|
||||
try {
|
||||
git stash pop
|
||||
Write-Host "✅ Local changes restored successfully"
|
||||
} catch {
|
||||
Write-Host "⚠️ Conflict when restoring local changes"
|
||||
# Save the conflicted state in a new branch for later review
|
||||
$branchName = "conflict-recovery-$(Get-Date -Format 'yyyyMMdd-HHmmss')"
|
||||
git checkout -b $branchName
|
||||
Write-Host "✅ Created branch '$branchName' with conflicted state"
|
||||
|
||||
# For test execution, revert to a clean state
|
||||
git reset --hard HEAD
|
||||
Write-Host "✅ Reset to clean state for test execution"
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
Write-Host "⚠️ Could not update repository: $_"
|
||||
Write-Host "Continuing with existing files..."
|
||||
}
|
||||
} else {
|
||||
Write-Host "⚠️ Not a Git repository, using existing files"
|
||||
}
|
||||
|
||||
- name: Verify Python Installation (Windows)
|
||||
if: matrix.os == 'windows'
|
||||
shell: pwsh
|
||||
run: |
|
||||
try {
|
||||
$pythonVersion = (python --version)
|
||||
Write-Host "✅ Python found: $pythonVersion"
|
||||
} catch {
|
||||
Write-Host "❌ Error: Python not found in PATH. Please install Python on this runner."
|
||||
exit 1
|
||||
}
|
||||
|
||||
- name: Setup Python Virtual Environment (Windows)
|
||||
if: matrix.os == 'windows'
|
||||
shell: pwsh
|
||||
run: |
|
||||
Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process -Force
|
||||
cd ${{ matrix.install-path }}
|
||||
|
||||
if (-Not (Test-Path -Path ".venv")) {
|
||||
Write-Host "Creating virtual environment..."
|
||||
python -m venv .venv
|
||||
} else {
|
||||
Write-Host "Using existing virtual environment"
|
||||
}
|
||||
|
||||
# Direct environment activation to avoid script execution issues
|
||||
$env:VIRTUAL_ENV = "$PWD\.venv"
|
||||
$env:PATH = "$env:VIRTUAL_ENV\Scripts;$env:PATH"
|
||||
|
||||
# Install dependencies
|
||||
if (Test-Path -Path "requirements.txt") {
|
||||
Write-Host "Installing dependencies from requirements.txt..."
|
||||
pip install -r requirements.txt
|
||||
|
||||
# Install Playwright browsers - add this line
|
||||
Write-Host "Installing Playwright browsers..."
|
||||
python -m playwright install
|
||||
} else {
|
||||
pip install outleap requests behave playwright
|
||||
# Install Playwright browsers - add this line
|
||||
Write-Host "Installing Playwright browsers..."
|
||||
python -m playwright install
|
||||
}
|
||||
|
||||
- name: Fetch & Download Installer Artifact (Windows)
|
||||
if: matrix.os == 'windows'
|
||||
shell: pwsh
|
||||
run: |
|
||||
$BUILD_ID = "${{ env.BUILD_ID }}"
|
||||
$ARTIFACTS_URL = "${{ env.ARTIFACTS_URL }}"
|
||||
|
||||
# Fetch the correct artifact URL
|
||||
$response = Invoke-RestMethod -Headers @{Authorization="token ${{ secrets.GITHUB_TOKEN }}" } -Uri $ARTIFACTS_URL
|
||||
$ARTIFACT_NAME = ($response.artifacts | Where-Object { $_.name -eq "Windows-installer" }).archive_download_url
|
||||
$ARTIFACT_NAME = ($response.artifacts | Where-Object { $_.name -eq "${{ matrix.artifact }}" }).archive_download_url
|
||||
|
||||
if (-Not $ARTIFACT_NAME) {
|
||||
Write-Host "❌ Error: Windows-installer artifact not found!"
|
||||
Write-Host "❌ Error: ${{ matrix.artifact }} artifact not found!"
|
||||
exit 1
|
||||
}
|
||||
|
||||
|
|
@ -80,16 +209,19 @@ jobs:
|
|||
|
||||
# Ensure download succeeded
|
||||
if (-Not (Test-Path $InstallerPath)) {
|
||||
Write-Host "❌ Error: Failed to download Windows-installer.zip"
|
||||
Write-Host "❌ Error: Failed to download ${{ matrix.artifact }}.zip"
|
||||
exit 1
|
||||
}
|
||||
|
||||
- name: Extract Installer & Locate Executable
|
||||
# Set the path for other steps
|
||||
echo "DOWNLOAD_PATH=$DownloadPath" | Out-File -FilePath $env:GITHUB_ENV -Append
|
||||
|
||||
- name: Extract Installer & Locate Executable (Windows)
|
||||
if: matrix.os == 'windows'
|
||||
shell: pwsh
|
||||
run: |
|
||||
# Explicitly set BUILD_ID again (since it does not appear to persist across steps)
|
||||
$BUILD_ID = "${{ github.event.workflow_run.id }}"
|
||||
$ExtractPath = "$env:TEMP\secondlife-build-$BUILD_ID"
|
||||
$BUILD_ID = "${{ env.BUILD_ID }}"
|
||||
$ExtractPath = "${{ env.DOWNLOAD_PATH }}"
|
||||
$InstallerZip = "$ExtractPath\installer.zip"
|
||||
|
||||
# Print paths for debugging
|
||||
|
|
@ -119,16 +251,19 @@ jobs:
|
|||
Write-Host "✅ Installer found: $INSTALLER_PATH"
|
||||
echo "INSTALLER_PATH=$INSTALLER_PATH" | Out-File -FilePath $env:GITHUB_ENV -Append
|
||||
|
||||
- name: Install Second Life Using Task Scheduler (Bypass UAC)
|
||||
- name: Install Second Life (Windows)
|
||||
if: matrix.os == 'windows'
|
||||
shell: pwsh
|
||||
run: |
|
||||
# Windows - Use Task Scheduler to bypass UAC
|
||||
$action = New-ScheduledTaskAction -Execute "${{ env.INSTALLER_PATH }}" -Argument "/S"
|
||||
$principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel Highest
|
||||
$task = New-ScheduledTask -Action $action -Principal $principal
|
||||
Register-ScheduledTask -TaskName "SilentSLInstaller" -InputObject $task -Force
|
||||
Start-ScheduledTask -TaskName "SilentSLInstaller"
|
||||
|
||||
- name: Wait for Installation to Complete
|
||||
- name: Wait for Installation to Complete (Windows)
|
||||
if: matrix.os == 'windows'
|
||||
shell: pwsh
|
||||
run: |
|
||||
Write-Host "Waiting for the Second Life installer to finish..."
|
||||
|
|
@ -139,18 +274,16 @@ jobs:
|
|||
|
||||
Write-Host "✅ Installation completed!"
|
||||
|
||||
- name: Cleanup Task Scheduler Entry
|
||||
- name: Cleanup After Installation (Windows)
|
||||
if: matrix.os == 'windows'
|
||||
shell: pwsh
|
||||
run: |
|
||||
# Cleanup Task Scheduler Entry
|
||||
Unregister-ScheduledTask -TaskName "SilentSLInstaller" -Confirm:$false
|
||||
Write-Host "✅ Task Scheduler entry removed."
|
||||
|
||||
- name: Delete Installer ZIP
|
||||
shell: pwsh
|
||||
run: |
|
||||
# Explicitly set BUILD_ID again
|
||||
$BUILD_ID = "${{ github.event.workflow_run.id }}"
|
||||
$DeletePath = "$env:TEMP\secondlife-build-$BUILD_ID\installer.zip"
|
||||
# Delete Installer ZIP
|
||||
$DeletePath = "${{ env.DOWNLOAD_PATH }}\installer.zip"
|
||||
|
||||
Write-Host "Checking if installer ZIP exists: $DeletePath"
|
||||
|
||||
|
|
@ -162,13 +295,281 @@ jobs:
|
|||
Write-Host "⚠️ Warning: ZIP file does not exist, skipping deletion."
|
||||
}
|
||||
|
||||
- name: Run QA Test Script
|
||||
- name: Run QA Test Script (Windows)
|
||||
if: matrix.os == 'windows'
|
||||
shell: pwsh
|
||||
run: |
|
||||
Write-Host "Running QA Test script..."
|
||||
python C:\viewer-sikulix-main\runTests.py
|
||||
Write-Host "Running QA Test script on Windows runner: ${{ matrix.runner }}..."
|
||||
cd ${{ matrix.install-path }}
|
||||
|
||||
# Activate virtual environment
|
||||
Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process -Force
|
||||
$env:VIRTUAL_ENV = "$PWD\.venv"
|
||||
$env:PATH = "$env:VIRTUAL_ENV\Scripts;$env:PATH"
|
||||
|
||||
# Set runner name as environment variable
|
||||
$env:RUNNER_NAME = "${{ matrix.runner }}"
|
||||
|
||||
# Run the test script
|
||||
python runTests.py
|
||||
|
||||
# Mac-specific steps
|
||||
- name: Set Build ID (Mac)
|
||||
if: matrix.os == 'mac'
|
||||
shell: bash
|
||||
run: |
|
||||
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
|
||||
echo "BUILD_ID=${{ github.event.inputs.build_id }}" >> $GITHUB_ENV
|
||||
echo "ARTIFACTS_URL=https://api.github.com/repos/secondlife/viewer/actions/runs/${{ github.event.inputs.build_id }}/artifacts" >> $GITHUB_ENV
|
||||
else
|
||||
echo "BUILD_ID=${{ github.event.workflow_run.id }}" >> $GITHUB_ENV
|
||||
echo "ARTIFACTS_URL=https://api.github.com/repos/secondlife/viewer/actions/runs/${{ github.event.workflow_run.id }}/artifacts" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
- name: Verify viewer-automation-main Exists (Mac)
|
||||
if: matrix.os == 'mac'
|
||||
shell: bash
|
||||
run: |
|
||||
if [ ! -d "${{ matrix.install-path }}" ]; then
|
||||
echo "❌ Error: viewer-automation folder not found on runner!"
|
||||
exit 1
|
||||
fi
|
||||
echo "✅ viewer-automation is provided."
|
||||
|
||||
- name: Verify viewer-automation-main is Up-To-Date (Mac)
|
||||
if: matrix.os == 'mac'
|
||||
shell: bash
|
||||
continue-on-error: true
|
||||
run: |
|
||||
cd ${{ matrix.install-path }}
|
||||
echo "Checking for repository updates..."
|
||||
|
||||
# Check if .git directory exists
|
||||
if [ -d ".git" ]; then
|
||||
# Save local changes instead of discarding them
|
||||
git stash push -m "Automated stash before update $(date)"
|
||||
echo "Local changes saved (if any)"
|
||||
|
||||
# Update the repository
|
||||
git pull || echo "⚠️ Could not update repository"
|
||||
echo "✅ Repository updated (or attempted update)"
|
||||
|
||||
# Try to restore local changes if any were stashed
|
||||
if git stash list | grep -q "Automated stash before update"; then
|
||||
# Try to pop the stash, but be prepared for conflicts
|
||||
if ! git stash pop; then
|
||||
echo "⚠️ Conflict when restoring local changes"
|
||||
# Save the conflicted state in a new branch for later review
|
||||
branch_name="conflict-recovery-$(date +%Y%m%d-%H%M%S)"
|
||||
git checkout -b "$branch_name"
|
||||
echo "✅ Created branch '$branch_name' with conflicted state"
|
||||
|
||||
# For test execution, revert to a clean state
|
||||
git reset --hard HEAD
|
||||
echo "✅ Reset to clean state for test execution"
|
||||
else
|
||||
echo "✅ Local changes restored successfully"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo "⚠️ Not a Git repository, using existing files"
|
||||
fi
|
||||
|
||||
- name: Verify Python Installation (Mac)
|
||||
if: matrix.os == 'mac'
|
||||
shell: bash
|
||||
run: |
|
||||
if command -v python3 &> /dev/null; then
|
||||
PYTHON_VERSION=$(python3 --version)
|
||||
echo "✅ Python found: $PYTHON_VERSION"
|
||||
else
|
||||
echo "❌ Error: Python3 not found in PATH. Please install Python on this runner."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Setup Python Virtual Environment (Mac)
|
||||
if: matrix.os == 'mac'
|
||||
shell: bash
|
||||
run: |
|
||||
cd ${{ matrix.install-path }}
|
||||
|
||||
# Create virtual environment if it doesn't exist
|
||||
if [ ! -d ".venv" ]; then
|
||||
echo "Creating virtual environment..."
|
||||
python3 -m venv .venv
|
||||
else
|
||||
echo "Using existing virtual environment"
|
||||
fi
|
||||
|
||||
# Activate virtual environment
|
||||
source .venv/bin/activate
|
||||
|
||||
# Install dependencies
|
||||
if [ -f "requirements.txt" ]; then
|
||||
pip install -r requirements.txt
|
||||
echo "✅ Installed dependencies from requirements.txt"
|
||||
|
||||
# Install Playwright browsers - add this line
|
||||
echo "Installing Playwright browsers..."
|
||||
python -m playwright install
|
||||
else
|
||||
pip install outleap requests behave playwright
|
||||
echo "⚠️ requirements.txt not found, installed basic dependencies"
|
||||
|
||||
# Install Playwright browsers - add this line
|
||||
echo "Installing Playwright browsers..."
|
||||
python -m playwright install
|
||||
fi
|
||||
|
||||
- name: Fetch & Download Installer Artifact (Mac)
|
||||
if: matrix.os == 'mac'
|
||||
shell: bash
|
||||
run: |
|
||||
# Mac-specific Bash commands
|
||||
response=$(curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -s ${{ env.ARTIFACTS_URL }})
|
||||
ARTIFACT_NAME=$(echo $response | jq -r '.artifacts[] | select(.name=="${{ matrix.artifact }}") | .archive_download_url')
|
||||
|
||||
if [ -z "$ARTIFACT_NAME" ]; then
|
||||
echo "❌ Error: ${{ matrix.artifact }} artifact not found!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ Artifact found: $ARTIFACT_NAME"
|
||||
|
||||
# Secure download path
|
||||
DOWNLOAD_PATH="/tmp/secondlife-build-${{ env.BUILD_ID }}"
|
||||
mkdir -p $DOWNLOAD_PATH
|
||||
INSTALLER_PATH="$DOWNLOAD_PATH/installer.zip"
|
||||
|
||||
# Download the ZIP
|
||||
curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -L $ARTIFACT_NAME -o $INSTALLER_PATH
|
||||
|
||||
# Ensure download succeeded
|
||||
if [ ! -f "$INSTALLER_PATH" ]; then
|
||||
echo "❌ Error: Failed to download ${{ matrix.artifact }}.zip"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Set the path for other steps
|
||||
echo "DOWNLOAD_PATH=$DOWNLOAD_PATH" >> $GITHUB_ENV
|
||||
|
||||
- name: Extract Installer & Locate Executable (Mac)
|
||||
if: matrix.os == 'mac'
|
||||
shell: bash
|
||||
run: |
|
||||
EXTRACT_PATH="${{ env.DOWNLOAD_PATH }}"
|
||||
INSTALLER_ZIP="$EXTRACT_PATH/installer.zip"
|
||||
|
||||
# Debug output
|
||||
echo "Extract Path: $EXTRACT_PATH"
|
||||
echo "Installer ZIP Path: $INSTALLER_ZIP"
|
||||
|
||||
# Verify ZIP exists
|
||||
if [ ! -f "$INSTALLER_ZIP" ]; then
|
||||
echo "❌ Error: ZIP file not found at $INSTALLER_ZIP!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ ZIP file exists and is valid. Extracting..."
|
||||
|
||||
# Extract the ZIP
|
||||
unzip -o "$INSTALLER_ZIP" -d "$EXTRACT_PATH"
|
||||
|
||||
# Find DMG file
|
||||
INSTALLER_PATH=$(find "$EXTRACT_PATH" -name "*.dmg" -type f | head -1)
|
||||
|
||||
if [ -z "$INSTALLER_PATH" ]; then
|
||||
echo "❌ Error: No installer DMG found in the extracted files!"
|
||||
echo "📂 Extracted Files:"
|
||||
ls -la "$EXTRACT_PATH"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ Installer found: $INSTALLER_PATH"
|
||||
echo "INSTALLER_PATH=$INSTALLER_PATH" >> $GITHUB_ENV
|
||||
|
||||
- name: Install Second Life (Mac)
|
||||
if: matrix.os == 'mac'
|
||||
shell: bash
|
||||
run: |
|
||||
# Mac installation
|
||||
echo "Mounting DMG installer..."
|
||||
MOUNT_POINT="/tmp/secondlife-dmg"
|
||||
mkdir -p "$MOUNT_POINT"
|
||||
|
||||
# Mount the DMG
|
||||
hdiutil attach "${{ env.INSTALLER_PATH }}" -mountpoint "$MOUNT_POINT" -nobrowse
|
||||
|
||||
echo "✅ DMG mounted at $MOUNT_POINT"
|
||||
|
||||
# Find the app in the mounted DMG
|
||||
APP_PATH=$(find "$MOUNT_POINT" -name "*.app" -type d | head -1)
|
||||
|
||||
if [ -z "$APP_PATH" ]; then
|
||||
echo "❌ Error: No .app bundle found in the mounted DMG!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Installing application to Applications folder..."
|
||||
|
||||
# Copy the app to the Applications folder (or specified install path)
|
||||
cp -R "$APP_PATH" "${{ matrix.install-path }}"
|
||||
|
||||
# Verify the app was copied successfully
|
||||
if [ ! -d "${{ matrix.install-path }}/$(basename "$APP_PATH")" ]; then
|
||||
echo "❌ Error: Failed to install application to ${{ matrix.install-path }}!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ Application installed successfully to ${{ matrix.install-path }}"
|
||||
|
||||
# Save mount point for cleanup
|
||||
echo "MOUNT_POINT=$MOUNT_POINT" >> $GITHUB_ENV
|
||||
|
||||
- name: Wait for Installation to Complete (Mac)
|
||||
if: matrix.os == 'mac'
|
||||
shell: bash
|
||||
run: |
|
||||
echo "Waiting for installation to complete..."
|
||||
# Sleep to allow installation to finish (adjust as needed)
|
||||
sleep 30
|
||||
echo "✅ Installation completed"
|
||||
|
||||
- name: Cleanup After Installation (Mac)
|
||||
if: matrix.os == 'mac'
|
||||
shell: bash
|
||||
run: |
|
||||
# Mac cleanup
|
||||
# Unmount the DMG
|
||||
echo "Unmounting DMG..."
|
||||
hdiutil detach "${{ env.MOUNT_POINT }}" -force
|
||||
|
||||
# Clean up temporary files
|
||||
echo "Cleaning up temporary files..."
|
||||
rm -rf "${{ env.DOWNLOAD_PATH }}"
|
||||
rm -rf "${{ env.MOUNT_POINT }}"
|
||||
|
||||
echo "✅ Cleanup completed"
|
||||
|
||||
- name: Run QA Test Script (Mac)
|
||||
if: matrix.os == 'mac'
|
||||
shell: bash
|
||||
run: |
|
||||
echo "Running QA Test script on Mac runner: ${{ matrix.runner }}..."
|
||||
cd ${{ matrix.install-path }}
|
||||
|
||||
# Activate virtual environment
|
||||
source .venv/bin/activate
|
||||
|
||||
# Set runner name as environment variable
|
||||
export RUNNER_NAME="${{ matrix.runner }}"
|
||||
|
||||
# Run the test script
|
||||
python runTests.py
|
||||
|
||||
# - name: Upload Test Results
|
||||
# uses: actions/upload-artifact@v3
|
||||
# if: always()
|
||||
# uses: actions/upload-artifact@v4
|
||||
# with:
|
||||
# name: test-results
|
||||
# path: C:\viewer-sikulix-main\regressionTest\test_results.html
|
||||
# name: test-results-${{ matrix.runner }}
|
||||
# path: ${{ matrix.install-path }}/regressionTest/test_results.html
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ repos:
|
|||
- id: indent-with-spaces
|
||||
files: \.(cpp|c|h|inl|py|glsl|cmake)$
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.4.0
|
||||
rev: v5.0.0
|
||||
hooks:
|
||||
- id: check-xml
|
||||
- id: mixed-line-ending
|
||||
|
|
|
|||
|
|
@ -0,0 +1,158 @@
|
|||
# Test plan for PRIM_MEDIA_FIRST_CLICK_INTERACT
|
||||
|
||||
## Requirements
|
||||
|
||||
- At least two accounts
|
||||
- At least one group
|
||||
- Land under your control
|
||||
|
||||
## Feature Brief
|
||||
|
||||
Historically media-on-a-prim (MOAP) in Second Life has been bound to a focus system which blocks mouse click/hover events, this feature creates exceptions to this focus system for a configurable set of objects to meet user preference.
|
||||
|
||||
## Testing
|
||||
|
||||
The following scripts and test cases cover each individual operational mode of the feature; in practice these modes can be combined by advanced users in any configuration they desire from debug settings. Even though the intended use case combines multiple modes, individual modes can be tested for functionality when tested as described below.
|
||||
|
||||
If testing an arbitrary combination of operational modes beyond what the GUI offers is desired, the parameters of the bitfield for calculation are located in lltoolpie.h under the MediaFirstClickTypes enum. As of writing there exists a total of ~127 possible unique/valid combinations, which is why testing each mode individually is considered the most efficient for a full functionality test.
|
||||
|
||||
### Scripts
|
||||
|
||||
#### Script A
|
||||
|
||||
This script creates a media surface that is eligible for media first-click interact. Depending on test conditions, this will exhibit new behavior.
|
||||
|
||||
```lsl
|
||||
default {
|
||||
state_entry() {
|
||||
llSetLinkMedia( LINK_THIS, 0, [
|
||||
PRIM_MEDIA_CURRENT_URL, "http://lecs-viewer-web-components.s3.amazonaws.com/v3.0/agni/avatars.html",
|
||||
PRIM_MEDIA_HOME_URL, "http://lecs-viewer-web-components.s3.amazonaws.com/v3.0/agni/avatars.html",
|
||||
PRIM_MEDIA_FIRST_CLICK_INTERACT, TRUE,
|
||||
PRIM_MEDIA_AUTO_PLAY, TRUE,
|
||||
PRIM_MEDIA_CONTROLS, PRIM_MEDIA_CONTROLS_MINI
|
||||
] );
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
#### Script B
|
||||
|
||||
This script creates a media surface that is NOT eligible for media first-click interact. In all but one test case, this will behave the same way.
|
||||
|
||||
```lsl
|
||||
default {
|
||||
state_entry() {
|
||||
llSetLinkMedia( LINK_THIS, 0, [
|
||||
PRIM_MEDIA_CURRENT_URL, "http://lecs-viewer-web-components.s3.amazonaws.com/v3.0/agni/avatars.html",
|
||||
PRIM_MEDIA_HOME_URL, "http://lecs-viewer-web-components.s3.amazonaws.com/v3.0/agni/avatars.html",
|
||||
PRIM_MEDIA_FIRST_CLICK_INTERACT, FALSE,
|
||||
PRIM_MEDIA_AUTO_PLAY, TRUE,
|
||||
PRIM_MEDIA_CONTROLS, PRIM_MEDIA_CONTROLS_MINI
|
||||
] );
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Standard testing procedure
|
||||
|
||||
You will be asked to enable media faces on multiple cubes, make sure that the webpage loads on each, and interact with them in the following ways.
|
||||
|
||||
1. Enable media for the cube, and verify that it displays a webpage.
|
||||
2. Click on the terrain to clear any focus.
|
||||
3. Hover your mouse over UI elements of the webpage, and **observe** if they highlight/react to the mouse cursor.
|
||||
4. If hover events are not registered, clicking on the webpage and then **observe** if they begin reacting to hover events.
|
||||
5. Clicking on the terrain to clear any focus once again.
|
||||
6. Clicking on a UI element of the webpage and **observe** if it reacts to the first click, or requires a second click. *(Maximum of 2 clicks per attempt)*
|
||||
|
||||
These steps will be repeated for one or more pairs of cubes per test case to ensure that media first click interact is functioning within expectations. Unless otherwise mentioned for a specific test case, you simply need only be in the same region as the cubes to test with them.
|
||||
|
||||
## Test cases
|
||||
|
||||
All test cases begin with at least two cubes rezzed, one containing Script A henceforth referred to as Cube A and one with Script B referred to as Cube B. The steps of some test cases may impact the condition of the cubes, so keeping a spare set rezzed or in inventory to rapidly duplicate should improve efficiency if testing cases in series.
|
||||
|
||||
### Case 1 (MEDIA_FIRST_CLICK_NONE)
|
||||
|
||||
Ensure that debug setting `MediaFirstClickInteract` is set to `0`
|
||||
|
||||
Starting with Cube A and Cube B, perform the testing procedure on each.
|
||||
|
||||
**Expected observations:** Both webpages do not react to hover events until clicked, both webpages do not react to clicks until clicked once to establish focus
|
||||
|
||||
### Case 2 (MEDIA_FIRST_CLICK_HUD)
|
||||
|
||||
Ensure that debug setting `MediaFirstClickInteract` is set to `1`
|
||||
|
||||
Starting with Cube A and Cube B, attach them both to your HUD and perform the testing procedure on each. You may need to rotate or scale the cubes to fit on your screen before beginning testing. You may attach both at the same time, or only one at a time.
|
||||
|
||||
**Expected observations:** The webpage on Cube A will react to mouse cursor hover events and clicks without needing a focus click, but the webpage on Cube B will not.
|
||||
|
||||
### Case 3 (MEDIA_FIRST_CLICK_OWN)
|
||||
|
||||
Ensure that debug setting `MediaFirstClickInteract` is set to `2`
|
||||
|
||||
This test case requires two pairs of cubes, and the second pair must not be owned by your testing account. What owns them is not important, it can be a group or your second testing account.
|
||||
|
||||
Perform the testing procedure on both sets of cubes.
|
||||
|
||||
**Expected observations:** The webpage on Cube A will react to mouse cursor hover events and clicks without needing a focus click, but the webpage on Cube B will not. The other pair of cubes will react the same as your Cube B.
|
||||
|
||||
### Case 4 (MEDIA_FIRST_CLICK_GROUP)
|
||||
|
||||
Ensure that debug setting `MediaFirstClickInteract` is set to `4`
|
||||
|
||||
This test case requires two cubes, and the second cube must be deeded or set to a group that your testing account is a member of. As long as the second set of cubes is set to a group that your test account is a member of, the avatar that owns them does not matter.
|
||||
|
||||
Perform the testing procedure on both sets of cubes.
|
||||
|
||||
**Expected observations:** The cube owned by your primary account will not react to mouse cursor hover events and clicks without needing a focus click. The cube set to group will react to mouse cursor hover events and clicks without needing a focus click.
|
||||
|
||||
### Case 5 (MEDIA_FIRST_CLICK_FRIEND)
|
||||
|
||||
Ensure that debug setting `MediaFirstClickInteract` is set to `8`
|
||||
|
||||
This test case requires three sets of cubes, one owned by you, one owned by another avatar on your friend list, and a third set owned by an avatar that is not on your friend list, or deeded to group. You can optionally use two sets of cubes, and dissolve friendship with your second account to test non-friend cubes.
|
||||
|
||||
Perform the testing procedure on all cubes
|
||||
|
||||
**Expected observations:** Cube A owned by a friended avatar will react to mouse cursor hover events and clicks without needing a focus click. All other cubes will not.
|
||||
|
||||
### Case 6 (MEDIA_FIRST_CLICK_LAND)
|
||||
|
||||
Ensure that debug setting `MediaFirstClickInteract` is set to `16`
|
||||
|
||||
This is the most tricky test case due to the multiple combinations that this operational mode considers valid. You will need multiple cubes, and can omit Cube B for brevity unless running a full test pass. This is probably most efficiently tested from your second account, using your first account to adjust the test parameters to fit other sub-cases.
|
||||
|
||||
Note: This requires the avatar that is performing the tests to physically be in the same parcel as the test cube(s). If you are standing outside of the parcel the media cubes are in, they will never react to mouse cursor hover events and clicks without needing a focus click under this operational mode.
|
||||
|
||||
1. Place down a set of cubes owned by the same avatar as the land
|
||||
- The second account should see Cube A react to mouse cursor hover events and clicks without needing a focus click
|
||||
- Cube B if tested, will not react in all further sub-cases and will not be mentioned further.
|
||||
|
||||
2. Adjust the conditions of the cubes and parcel such that they are owned by another avatar, but have the same group as the land set
|
||||
- The second account should see Cube A react to mouse cursor hover events and clicks without needing a focus click
|
||||
|
||||
3. Adjust the conditions of the cubes and parcel such that they are deeded to the same group that the parcel is deeded to
|
||||
- The second account should see Cube A react to mouse cursor hover events and clicks without needing a focus click
|
||||
|
||||
4. Adjust the conditions of the cubes and parcel such that the parcel and cubes do not share an owner, or a group
|
||||
- The second account should see Cube A NOT react to mouse cursor hover events until clicked, and clicks WILL need a focus click before registering.
|
||||
|
||||
### Case 7 (MEDIA_FIRST_CLICK_ANY) (optional)
|
||||
|
||||
Ensure that debug setting `MediaFirstClickInteract` is set to `32767`
|
||||
|
||||
Repeat test cases 1-6.
|
||||
|
||||
1. Test case 1 should fail
|
||||
2. Test cases 2-6 should pass
|
||||
|
||||
### Case 8 (MEDIA_FIRST_CLICK_BYPASS_MOAP_FLAG) (optional)
|
||||
|
||||
Ensure that debug setting `MediaFirstClickInteract` is set to `65535`
|
||||
|
||||
Repeat test cases 1-6, there is no pass/fail for this run.
|
||||
|
||||
All cubes including B types should exhibit the same first-click interact behavior.
|
||||
|
|
@ -72,7 +72,6 @@ else()
|
|||
find_library(COCOA_LIBRARY Cocoa)
|
||||
find_library(IOKIT_LIBRARY IOKit)
|
||||
|
||||
find_library(AGL_LIBRARY AGL)
|
||||
find_library(APPKIT_LIBRARY AppKit)
|
||||
find_library(COREAUDIO_LIBRARY CoreAudio)
|
||||
|
||||
|
|
@ -81,7 +80,6 @@ else()
|
|||
${IOKIT_LIBRARY}
|
||||
${COREFOUNDATION_LIBRARY}
|
||||
${CARBON_LIBRARY}
|
||||
${AGL_LIBRARY}
|
||||
${APPKIT_LIBRARY}
|
||||
${COREAUDIO_LIBRARY}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ set(llappearance_SOURCE_FILES
|
|||
llavatarjoint.cpp
|
||||
llavatarjointmesh.cpp
|
||||
lldriverparam.cpp
|
||||
lljointdata.h
|
||||
lllocaltextureobject.cpp
|
||||
llpolyskeletaldistortion.cpp
|
||||
llpolymesh.cpp
|
||||
|
|
|
|||
|
|
@ -29,16 +29,17 @@
|
|||
#include "llavatarappearance.h"
|
||||
#include "llavatarappearancedefines.h"
|
||||
#include "llavatarjointmesh.h"
|
||||
#include "lljointdata.h"
|
||||
#include "llstl.h"
|
||||
#include "lldir.h"
|
||||
#include "llpolymorph.h"
|
||||
#include "llpolymesh.h"
|
||||
#include "llpolyskeletaldistortion.h"
|
||||
#include "llstl.h"
|
||||
#include "lltexglobalcolor.h"
|
||||
#include "llwearabledata.h"
|
||||
#include "boost/bind.hpp"
|
||||
#include "boost/tokenizer.hpp"
|
||||
#include "v4math.h"
|
||||
|
||||
using namespace LLAvatarAppearanceDefines;
|
||||
|
||||
|
|
@ -71,11 +72,13 @@ public:
|
|||
mChildren.clear();
|
||||
}
|
||||
bool parseXml(LLXmlTreeNode* node);
|
||||
glm::mat4 getJointMatrix();
|
||||
|
||||
private:
|
||||
std::string mName;
|
||||
std::string mSupport;
|
||||
std::string mAliases;
|
||||
std::string mGroup;
|
||||
bool mIsJoint;
|
||||
LLVector3 mPos;
|
||||
LLVector3 mEnd;
|
||||
|
|
@ -105,11 +108,17 @@ public:
|
|||
S32 getNumBones() const { return mNumBones; }
|
||||
S32 getNumCollisionVolumes() const { return mNumCollisionVolumes; }
|
||||
|
||||
private:
|
||||
typedef std::vector<LLAvatarBoneInfo*> bone_info_list_t;
|
||||
static void getJointMatricesAndHierarhy(
|
||||
LLAvatarBoneInfo* bone_info,
|
||||
LLJointData& data,
|
||||
const glm::mat4& parent_mat);
|
||||
|
||||
private:
|
||||
S32 mNumBones;
|
||||
S32 mNumCollisionVolumes;
|
||||
LLAvatarAppearance::joint_alias_map_t mJointAliasMap;
|
||||
typedef std::vector<LLAvatarBoneInfo*> bone_info_list_t;
|
||||
bone_info_list_t mBoneInfoList;
|
||||
};
|
||||
|
||||
|
|
@ -1622,6 +1631,15 @@ bool LLAvatarBoneInfo::parseXml(LLXmlTreeNode* node)
|
|||
mSupport = "base";
|
||||
}
|
||||
|
||||
// Skeleton has 133 bones, but shader only allows 110 (LL_MAX_JOINTS_PER_MESH_OBJECT)
|
||||
// Groups can be used by importer to cut out unused groups of joints
|
||||
static LLStdStringHandle group_string = LLXmlTree::addAttributeString("group");
|
||||
if (!node->getFastAttributeString(group_string, mGroup))
|
||||
{
|
||||
LL_WARNS() << "Bone without group " << mName << LL_ENDL;
|
||||
mGroup = "global";
|
||||
}
|
||||
|
||||
if (mIsJoint)
|
||||
{
|
||||
static LLStdStringHandle pivot_string = LLXmlTree::addAttributeString("pivot");
|
||||
|
|
@ -1647,6 +1665,21 @@ bool LLAvatarBoneInfo::parseXml(LLXmlTreeNode* node)
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
glm::mat4 LLAvatarBoneInfo::getJointMatrix()
|
||||
{
|
||||
glm::mat4 mat(1.0f);
|
||||
// 1. Scaling
|
||||
mat = glm::scale(mat, glm::vec3(mScale[0], mScale[1], mScale[2]));
|
||||
// 2. Rotation (Euler angles rad)
|
||||
mat = glm::rotate(mat, mRot[0], glm::vec3(1, 0, 0));
|
||||
mat = glm::rotate(mat, mRot[1], glm::vec3(0, 1, 0));
|
||||
mat = glm::rotate(mat, mRot[2], glm::vec3(0, 0, 1));
|
||||
// 3. Position
|
||||
mat = glm::translate(mat, glm::vec3(mPos[0], mPos[1], mPos[2]));
|
||||
return mat;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// LLAvatarSkeletonInfo::parseXml()
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
@ -1677,6 +1710,25 @@ bool LLAvatarSkeletonInfo::parseXml(LLXmlTreeNode* node)
|
|||
return true;
|
||||
}
|
||||
|
||||
void LLAvatarSkeletonInfo::getJointMatricesAndHierarhy(
|
||||
LLAvatarBoneInfo* bone_info,
|
||||
LLJointData& data,
|
||||
const glm::mat4& parent_mat)
|
||||
{
|
||||
data.mName = bone_info->mName;
|
||||
data.mJointMatrix = bone_info->getJointMatrix();
|
||||
data.mScale = glm::vec3(bone_info->mScale[0], bone_info->mScale[1], bone_info->mScale[2]);
|
||||
data.mRotation = bone_info->mRot;
|
||||
data.mRestMatrix = parent_mat * data.mJointMatrix;
|
||||
data.mIsJoint = bone_info->mIsJoint;
|
||||
data.mGroup = bone_info->mGroup;
|
||||
for (LLAvatarBoneInfo* child_info : bone_info->mChildren)
|
||||
{
|
||||
LLJointData& child_data = data.mChildren.emplace_back();
|
||||
getJointMatricesAndHierarhy(child_info, child_data, data.mRestMatrix);
|
||||
}
|
||||
}
|
||||
|
||||
//Make aliases for joint and push to map.
|
||||
void LLAvatarAppearance::makeJointAliases(LLAvatarBoneInfo *bone_info)
|
||||
{
|
||||
|
|
@ -1738,6 +1790,16 @@ const LLAvatarAppearance::joint_alias_map_t& LLAvatarAppearance::getJointAliases
|
|||
return mJointAliasMap;
|
||||
}
|
||||
|
||||
void LLAvatarAppearance::getJointMatricesAndHierarhy(std::vector<LLJointData> &data) const
|
||||
{
|
||||
glm::mat4 identity(1.f);
|
||||
for (LLAvatarBoneInfo* bone_info : sAvatarSkeletonInfo->mBoneInfoList)
|
||||
{
|
||||
LLJointData& child_data = data.emplace_back();
|
||||
LLAvatarSkeletonInfo::getJointMatricesAndHierarhy(bone_info, child_data, identity);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// parseXmlSkeletonNode(): parses <skeleton> nodes from XML tree
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
#include "lltexlayer.h"
|
||||
#include "llviewervisualparam.h"
|
||||
#include "llxmltree.h"
|
||||
#include "v4math.h"
|
||||
|
||||
class LLTexLayerSet;
|
||||
class LLTexGlobalColor;
|
||||
|
|
@ -41,6 +42,7 @@ class LLTexGlobalColorInfo;
|
|||
class LLWearableData;
|
||||
class LLAvatarBoneInfo;
|
||||
class LLAvatarSkeletonInfo;
|
||||
class LLJointData;
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// LLAvatarAppearance
|
||||
|
|
@ -140,10 +142,7 @@ public:
|
|||
LLVector3 mHeadOffset{}; // current head position
|
||||
LLAvatarJoint* mRoot{ nullptr };
|
||||
|
||||
//<FS:Ansariel> Joint-lookup improvements
|
||||
// typedef std::map<std::string, LLJoint*> joint_map_t;
|
||||
typedef std::map<std::string, LLJoint*, std::less<>> joint_map_t;
|
||||
|
||||
joint_map_t mJointMap;
|
||||
|
||||
typedef std::map<std::string, LLVector3> joint_state_map_t;
|
||||
|
|
@ -158,7 +157,9 @@ public:
|
|||
const avatar_joint_list_t& getSkeleton() { return mSkeleton; }
|
||||
typedef std::map<std::string, std::string, std::less<>> joint_alias_map_t;
|
||||
const joint_alias_map_t& getJointAliases();
|
||||
|
||||
typedef std::map<std::string, std::string> joint_parent_map_t; // matrix plus parent
|
||||
typedef std::map<std::string, glm::mat4> joint_rest_map_t;
|
||||
void getJointMatricesAndHierarhy(std::vector<LLJointData> &data) const;
|
||||
|
||||
protected:
|
||||
static bool parseSkeletonFile(const std::string& filename, LLXmlTree& skeleton_xml_tree);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,66 @@
|
|||
/**
|
||||
* @file lljointdata.h
|
||||
* @brief LLJointData class for holding individual joint data and skeleton
|
||||
*
|
||||
* $LicenseInfo:firstyear=2025&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2025, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#ifndef LL_LLJOINTDATA_H
|
||||
#define LL_LLJOINTDATA_H
|
||||
|
||||
#include "v4math.h"
|
||||
|
||||
// may be just move LLAvatarBoneInfo
|
||||
class LLJointData
|
||||
{
|
||||
public:
|
||||
std::string mName;
|
||||
std::string mGroup;
|
||||
glm::mat4 mJointMatrix;
|
||||
glm::mat4 mRestMatrix;
|
||||
glm::vec3 mScale;
|
||||
LLVector3 mRotation;
|
||||
|
||||
typedef std::vector<LLJointData> bones_t;
|
||||
bones_t mChildren;
|
||||
|
||||
bool mIsJoint; // if not, collision_volume
|
||||
enum SupportCategory
|
||||
{
|
||||
SUPPORT_BASE,
|
||||
SUPPORT_EXTENDED
|
||||
};
|
||||
SupportCategory mSupport;
|
||||
void setSupport(const std::string& support)
|
||||
{
|
||||
if (support == "extended")
|
||||
{
|
||||
mSupport = SUPPORT_EXTENDED;
|
||||
}
|
||||
else
|
||||
{
|
||||
mSupport = SUPPORT_BASE;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif //LL_LLJOINTDATA_H
|
||||
|
|
@ -1300,7 +1300,7 @@ void LLTexLayer::renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLC
|
|||
{
|
||||
if (!force_render && !hasMorph())
|
||||
{
|
||||
LL_DEBUGS() << "skipping renderMorphMasks for " << getUUID() << LL_ENDL;
|
||||
LL_DEBUGS("Morph") << "skipping renderMorphMasks for " << getUUID() << LL_ENDL;
|
||||
return;
|
||||
}
|
||||
LL_PROFILE_ZONE_SCOPED;
|
||||
|
|
@ -1332,7 +1332,7 @@ void LLTexLayer::renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLC
|
|||
success &= param->render( x, y, width, height );
|
||||
if (!success && !force_render)
|
||||
{
|
||||
LL_DEBUGS() << "Failed to render param " << param->getID() << " ; skipping morph mask." << LL_ENDL;
|
||||
LL_DEBUGS("Morph") << "Failed to render param " << param->getID() << " ; skipping morph mask." << LL_ENDL;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -1372,7 +1372,7 @@ void LLTexLayer::renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLC
|
|||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS() << "Skipping rendering of " << getInfo()->mStaticImageFileName
|
||||
LL_WARNS("Morph") << "Skipping rendering of " << getInfo()->mStaticImageFileName
|
||||
<< "; expected 1 or 4 components." << LL_ENDL;
|
||||
}
|
||||
}
|
||||
|
|
@ -1411,8 +1411,8 @@ void LLTexLayer::renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLC
|
|||
// We can get bad morph masks during login, on minimize, and occasional gl errors.
|
||||
// We should only be doing this when we believe something has changed with respect to the user's appearance.
|
||||
{
|
||||
LL_DEBUGS("Avatar") << "gl alpha cache of morph mask not found, doing readback: " << getName() << LL_ENDL;
|
||||
// clear out a slot if we have filled our cache
|
||||
LL_DEBUGS("Morph") << "gl alpha cache of morph mask not found, doing readback: " << getName() << LL_ENDL;
|
||||
// clear out a slot if we have filled our cache
|
||||
S32 max_cache_entries = getTexLayerSet()->getAvatarAppearance()->isSelf() ? 4 : 1;
|
||||
while ((S32)mAlphaCache.size() >= max_cache_entries)
|
||||
{
|
||||
|
|
@ -1496,29 +1496,31 @@ void LLTexLayer::renderMorphMasks(S32 x, S32 y, S32 width, S32 height, const LLC
|
|||
// </FS:ND>
|
||||
|
||||
glGetTexImage(LLTexUnit::getInternalType(LLTexUnit::TT_TEXTURE), 0, GL_RGBA, GL_UNSIGNED_BYTE, temp);
|
||||
|
||||
U8* alpha_cursor = alpha_data;
|
||||
U8* pixel = temp;
|
||||
|
||||
// <FS:ND> Copy out data. Buffer can be bigger than alpha_cursor, so need to skip pixel
|
||||
|
||||
// for (int i = 0; i < pixels; i++)
|
||||
// {
|
||||
// *alpha_cursor++ = pixel[3];
|
||||
// pixel += 4;
|
||||
// }
|
||||
|
||||
for (int i = 0; i < height; ++i)
|
||||
GLenum error = glGetError();
|
||||
if (error != GL_NO_ERROR)
|
||||
{
|
||||
for (int j = 0; j < width; ++j)
|
||||
{
|
||||
*alpha_cursor++ = pixel[3];
|
||||
pixel += 4;
|
||||
}
|
||||
|
||||
pixel += (textureW - width) * 4;;
|
||||
LL_INFOS("Morph") << "GL Error while reading back morph texture. Error code: " << error << LL_ENDL;
|
||||
}
|
||||
else
|
||||
{
|
||||
U8* alpha_cursor = alpha_data;
|
||||
U8* pixel = temp;
|
||||
// <FS:ND> Copy out data. Buffer can be bigger than alpha_cursor, so need to skip pixel
|
||||
//for (int i = 0; i < pixels; i++)
|
||||
//{
|
||||
// *alpha_cursor++ = pixel[3];
|
||||
// pixel += 4;
|
||||
//}
|
||||
for (int i = 0; i < height; ++i)
|
||||
{
|
||||
for (int j = 0; j < width; ++j)
|
||||
{
|
||||
*alpha_cursor++ = pixel[3];
|
||||
pixel += 4;
|
||||
}
|
||||
}
|
||||
// </FS:ND>
|
||||
}
|
||||
// </FS:ND>
|
||||
|
||||
gGL.getTexUnit(0)->disable();
|
||||
|
||||
|
|
|
|||
|
|
@ -77,14 +77,11 @@ LLCharacter::~LLCharacter()
|
|||
//-----------------------------------------------------------------------------
|
||||
// getJoint()
|
||||
//-----------------------------------------------------------------------------
|
||||
//<FS:Ansariel> Joint-lookup improvements
|
||||
//LLJoint *LLCharacter::getJoint( const std::string &name )
|
||||
LLJoint* LLCharacter::getJoint(std::string_view name)
|
||||
{
|
||||
LLJoint* joint = NULL;
|
||||
LLJoint* joint = nullptr;
|
||||
|
||||
LLJoint *root = getRootJoint();
|
||||
if (root)
|
||||
if (LLJoint* root = getRootJoint())
|
||||
{
|
||||
joint = root->findJoint(name);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,8 +76,6 @@ public:
|
|||
// get the specified joint
|
||||
// default implementation does recursive search,
|
||||
// subclasses may optimize/cache results.
|
||||
//<FS:Ansariel> Joint-lookup improvements
|
||||
// virtual LLJoint *getJoint( const std::string &name );
|
||||
virtual LLJoint* getJoint(std::string_view name);
|
||||
|
||||
// get the position of the character
|
||||
|
|
|
|||
|
|
@ -242,9 +242,7 @@ LLJoint *LLJoint::getRoot()
|
|||
//-----------------------------------------------------------------------------
|
||||
// findJoint()
|
||||
//-----------------------------------------------------------------------------
|
||||
//<FS:Ansariel> Joint-lookup improvements
|
||||
//LLJoint *LLJoint::findJoint( const std::string &name )
|
||||
LLJoint *LLJoint::findJoint(std::string_view name)
|
||||
LLJoint* LLJoint::findJoint(std::string_view name)
|
||||
{
|
||||
if (name == getName())
|
||||
return this;
|
||||
|
|
@ -253,15 +251,14 @@ LLJoint *LLJoint::findJoint(std::string_view name)
|
|||
{
|
||||
if(joint)
|
||||
{
|
||||
LLJoint *found = joint->findJoint(name);
|
||||
if (found)
|
||||
if (LLJoint* found = joint->findJoint(name))
|
||||
{
|
||||
return found;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -222,8 +222,6 @@ public:
|
|||
LLJoint *getRoot();
|
||||
|
||||
// search for child joints by name
|
||||
//<FS:Ansariel> Joint-lookup improvements
|
||||
//LLJoint *findJoint( const std::string &name );
|
||||
LLJoint* findJoint(std::string_view name);
|
||||
|
||||
// add/remove children
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
|
||||
#include "apr_portable.h"
|
||||
|
||||
#include "llapp.h"
|
||||
#include "llthread.h"
|
||||
#include "llmutex.h"
|
||||
|
||||
|
|
@ -35,6 +36,7 @@
|
|||
#include "lltrace.h"
|
||||
#include "lltracethreadrecorder.h"
|
||||
#include "llexception.h"
|
||||
#include "workqueue.h"
|
||||
|
||||
#if LL_LINUX
|
||||
#include <sched.h>
|
||||
|
|
@ -106,6 +108,27 @@ namespace
|
|||
return s_thread_id;
|
||||
}
|
||||
|
||||
#if LL_WINDOWS
|
||||
|
||||
static const U32 STATUS_MSC_EXCEPTION = 0xE06D7363; // compiler specific
|
||||
|
||||
U32 exception_filter(U32 code, struct _EXCEPTION_POINTERS* exception_infop)
|
||||
{
|
||||
if (LLApp::instance()->reportCrashToBugsplat((void*)exception_infop))
|
||||
{
|
||||
// Handled
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
else if (code == STATUS_MSC_EXCEPTION)
|
||||
{
|
||||
// C++ exception, go on
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
// handle it, convert to std::exception
|
||||
return EXCEPTION_EXECUTE_HANDLER;
|
||||
}
|
||||
#endif // LL_WINDOWS
|
||||
} // anonymous namespace
|
||||
|
||||
LL_COMMON_API bool on_main_thread()
|
||||
|
|
@ -160,22 +183,11 @@ void LLThread::threadRun()
|
|||
// Run the user supplied function
|
||||
do
|
||||
{
|
||||
try
|
||||
{
|
||||
LL_PROFILER_THREAD_BEGIN(mName.c_str())
|
||||
run();
|
||||
LL_PROFILER_THREAD_END(mName.c_str())
|
||||
}
|
||||
catch (const LLContinueError &e)
|
||||
{
|
||||
LL_WARNS("THREAD") << "ContinueException on thread '" << mName <<
|
||||
"' reentering run(). Error what is: '" << e.what() << "'" << LL_ENDL;
|
||||
//output possible call stacks to log file.
|
||||
LLError::LLCallStacks::print();
|
||||
|
||||
LOG_UNHANDLED_EXCEPTION("LLThread");
|
||||
continue;
|
||||
}
|
||||
#ifdef LL_WINDOWS
|
||||
sehHandle(); // Structured Exception Handling
|
||||
#else
|
||||
tryRun();
|
||||
#endif
|
||||
break;
|
||||
|
||||
} while (true);
|
||||
|
|
@ -193,6 +205,72 @@ void LLThread::threadRun()
|
|||
mStatus = STOPPED;
|
||||
}
|
||||
|
||||
void LLThread::tryRun()
|
||||
{
|
||||
try
|
||||
{
|
||||
LL_PROFILER_THREAD_BEGIN(mName.c_str())
|
||||
run();
|
||||
LL_PROFILER_THREAD_END(mName.c_str())
|
||||
|
||||
}
|
||||
catch (const LLContinueError& e)
|
||||
{
|
||||
LL_WARNS("THREAD") << "ContinueException on thread '" << mName <<
|
||||
"'. Error what is: '" << e.what() << "'" << LL_ENDL;
|
||||
LLError::LLCallStacks::print();
|
||||
|
||||
LOG_UNHANDLED_EXCEPTION("LLThread");
|
||||
}
|
||||
catch (std::bad_alloc&)
|
||||
{
|
||||
// Todo: improve this, this is going to have a different callstack
|
||||
// instead of showing where it crashed
|
||||
LL_WARNS("THREAD") << "Out of memory in a thread: " << mName << LL_ENDL;
|
||||
|
||||
LL::WorkQueue::ptr_t main_queue = LL::WorkQueue::getInstance("mainloop");
|
||||
main_queue->post(
|
||||
// Bind the current exception, rethrow it in main loop.
|
||||
[]() {
|
||||
LLError::LLUserWarningMsg::showOutOfMemory();
|
||||
LL_ERRS("THREAD") << "Out of memory in a thread" << LL_ENDL;
|
||||
});
|
||||
}
|
||||
#ifndef LL_WINDOWS
|
||||
catch (...)
|
||||
{
|
||||
// Stash any other kind of uncaught exception to be rethrown by main thread.
|
||||
LL_WARNS("THREAD") << "Capturing and rethrowing uncaught exception in LLThread "
|
||||
<< mName << LL_ENDL;
|
||||
|
||||
LL::WorkQueue::ptr_t main_queue = LL::WorkQueue::getInstance("mainloop");
|
||||
main_queue->post(
|
||||
// Bind the current exception, rethrow it in main loop.
|
||||
[exc = std::current_exception()]() { std::rethrow_exception(exc); });
|
||||
}
|
||||
#endif // else LL_WINDOWS
|
||||
}
|
||||
|
||||
#ifdef LL_WINDOWS
|
||||
void LLThread::sehHandle()
|
||||
{
|
||||
__try
|
||||
{
|
||||
// handle stop and continue exceptions first
|
||||
tryRun();
|
||||
}
|
||||
__except (exception_filter(GetExceptionCode(), GetExceptionInformation()))
|
||||
{
|
||||
// convert to C++ styled exception
|
||||
// Note: it might be better to use _se_set_translator
|
||||
// if you want exception to inherit full callstack
|
||||
char integer_string[512];
|
||||
sprintf(integer_string, "SEH, code: %lu\n", GetExceptionCode());
|
||||
throw std::exception(integer_string);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
LLThread::LLThread(const std::string& name, apr_pool_t *poolp) :
|
||||
mPaused(false),
|
||||
mName(name),
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ public:
|
|||
// Called from MAIN THREAD.
|
||||
void pause();
|
||||
void unpause();
|
||||
bool isPaused() { return isStopped() || mPaused; }
|
||||
bool isPaused() const { return isStopped() || mPaused; }
|
||||
|
||||
// Cause the thread to wake up and check its condition
|
||||
void wake();
|
||||
|
|
@ -98,6 +98,11 @@ private:
|
|||
|
||||
// static function passed to APR thread creation routine
|
||||
void threadRun();
|
||||
void tryRun();
|
||||
|
||||
#ifdef LL_WINDOWS
|
||||
void sehHandle();
|
||||
#endif
|
||||
|
||||
protected:
|
||||
std::string mName;
|
||||
|
|
|
|||
|
|
@ -182,14 +182,22 @@ void LL::WorkQueueBase::callWork(const Work& work)
|
|||
}
|
||||
catch (...)
|
||||
{
|
||||
// Stash any other kind of uncaught exception to be rethrown by main thread.
|
||||
LL_WARNS("LLCoros") << "Capturing and rethrowing uncaught exception in WorkQueueBase "
|
||||
<< getKey() << LL_ENDL;
|
||||
if (getKey() != "mainloop")
|
||||
{
|
||||
// Stash any other kind of uncaught exception to be rethrown by main thread.
|
||||
LL_WARNS("LLCoros") << "Capturing and rethrowing uncaught exception in WorkQueueBase "
|
||||
<< getKey() << LL_ENDL;
|
||||
|
||||
LL::WorkQueue::ptr_t main_queue = LL::WorkQueue::getInstance("mainloop");
|
||||
main_queue->post(
|
||||
// Bind the current exception, rethrow it in main loop.
|
||||
[exc = std::current_exception()]() { std::rethrow_exception(exc); });
|
||||
LL::WorkQueue::ptr_t main_queue = LL::WorkQueue::getInstance("mainloop");
|
||||
main_queue->post(
|
||||
// Bind the current exception, rethrow it in main loop.
|
||||
[exc = std::current_exception()]() { std::rethrow_exception(exc); });
|
||||
}
|
||||
else
|
||||
{
|
||||
// let main loop crash
|
||||
throw;
|
||||
}
|
||||
}
|
||||
#endif // else LL_WINDOWS
|
||||
}
|
||||
|
|
|
|||
|
|
@ -114,9 +114,10 @@ std::vector<std::string> LLDir::getFilesInDir(const std::string &dirname)
|
|||
|
||||
std::vector<std::string> v;
|
||||
|
||||
if (exists(p))
|
||||
boost::system::error_code ec;
|
||||
if (exists(p, ec) && !ec.failed())
|
||||
{
|
||||
if (is_directory(p))
|
||||
if (is_directory(p, ec) && !ec.failed())
|
||||
{
|
||||
boost::filesystem::directory_iterator end_iter;
|
||||
for (boost::filesystem::directory_iterator dir_itr(p);
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ bool LLImageDimensionsInfo::load(const std::string& src_filename,U32 codec)
|
|||
bool LLImageDimensionsInfo::getImageDimensionsBmp()
|
||||
{
|
||||
// Make sure the file is long enough.
|
||||
const S32 DATA_LEN = 26; // BMP header (14) + DIB header size (4) + width (4) + height (4)
|
||||
constexpr S32 DATA_LEN = 26; // BMP header (14) + DIB header size (4) + width (4) + height (4)
|
||||
if (!checkFileLength(DATA_LEN))
|
||||
{
|
||||
LL_WARNS() << "Premature end of file" << LL_ENDL;
|
||||
|
|
@ -105,7 +105,7 @@ bool LLImageDimensionsInfo::getImageDimensionsBmp()
|
|||
|
||||
bool LLImageDimensionsInfo::getImageDimensionsTga()
|
||||
{
|
||||
const S32 TGA_FILE_HEADER_SIZE = 12;
|
||||
constexpr S32 TGA_FILE_HEADER_SIZE = 12;
|
||||
|
||||
// Make sure the file is long enough.
|
||||
if (!checkFileLength(TGA_FILE_HEADER_SIZE + 1 /* width */ + 1 /* height */))
|
||||
|
|
@ -124,7 +124,7 @@ bool LLImageDimensionsInfo::getImageDimensionsTga()
|
|||
|
||||
bool LLImageDimensionsInfo::getImageDimensionsPng()
|
||||
{
|
||||
const S32 PNG_MAGIC_SIZE = 8;
|
||||
constexpr S32 PNG_MAGIC_SIZE = 8;
|
||||
|
||||
// Make sure the file is long enough.
|
||||
if (!checkFileLength(PNG_MAGIC_SIZE + 8 + sizeof(S32) * 2 /* width, height */))
|
||||
|
|
@ -134,7 +134,7 @@ bool LLImageDimensionsInfo::getImageDimensionsPng()
|
|||
}
|
||||
|
||||
// Read PNG signature.
|
||||
const U8 png_magic[PNG_MAGIC_SIZE] = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};
|
||||
constexpr U8 png_magic[PNG_MAGIC_SIZE] = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};
|
||||
U8 signature[PNG_MAGIC_SIZE];
|
||||
mInfile.read((void*)signature, PNG_MAGIC_SIZE);
|
||||
|
||||
|
|
@ -166,36 +166,36 @@ bool LLImageDimensionsInfo::getImageDimensionsJpeg()
|
|||
{
|
||||
sJpegErrorEncountered = false;
|
||||
clean();
|
||||
FILE *fp = LLFile::fopen(mSrcFilename, "rb");
|
||||
if (fp == NULL)
|
||||
FILE* fp = LLFile::fopen(mSrcFilename, "rb");
|
||||
if (!fp)
|
||||
{
|
||||
setLastError("Unable to open file for reading", mSrcFilename);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Make sure this is a JPEG file. */
|
||||
const size_t JPEG_MAGIC_SIZE = 2;
|
||||
const U8 jpeg_magic[JPEG_MAGIC_SIZE] = {0xFF, 0xD8};
|
||||
constexpr size_t JPEG_MAGIC_SIZE = 2;
|
||||
constexpr U8 jpeg_magic[JPEG_MAGIC_SIZE] = {0xFF, 0xD8};
|
||||
U8 signature[JPEG_MAGIC_SIZE];
|
||||
|
||||
if (fread(signature, sizeof(signature), 1, fp) != 1)
|
||||
{
|
||||
LL_WARNS() << "Premature end of file" << LL_ENDL;
|
||||
fclose(fp); // <FS:Ansariel> Don't leak the file handle
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
if (memcmp(signature, jpeg_magic, JPEG_MAGIC_SIZE) != 0)
|
||||
{
|
||||
LL_WARNS() << "Not a JPEG" << LL_ENDL;
|
||||
mWarning = "texture_load_format_error";
|
||||
fclose(fp); // <FS:ND/> Don't leak the file handle
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
fseek(fp, 0, SEEK_SET); // go back to start of the file
|
||||
|
||||
/* Init jpeg */
|
||||
jpeg_error_mgr jerr;
|
||||
jpeg_decompress_struct cinfo;
|
||||
jpeg_decompress_struct cinfo{};
|
||||
cinfo.err = jpeg_std_error(&jerr);
|
||||
// Call our function instead of exit() if Libjpeg encounters an error.
|
||||
// This is done to avoid crash in this case (STORM-472).
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ class LLImageDimensionsInfo
|
|||
{
|
||||
public:
|
||||
LLImageDimensionsInfo():
|
||||
mData(NULL)
|
||||
mData(nullptr)
|
||||
,mHeight(0)
|
||||
,mWidth(0)
|
||||
{}
|
||||
|
|
@ -67,7 +67,7 @@ protected:
|
|||
{
|
||||
mInfile.close();
|
||||
delete[] mData;
|
||||
mData = NULL;
|
||||
mData = nullptr;
|
||||
mWidth = 0;
|
||||
mHeight = 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -281,10 +281,11 @@ S32 LLImageJ2C::calcDataSizeJ2C(S32 w, S32 h, S32 comp, S32 discard_level, F32 r
|
|||
S32 height = (h > 0) ? h : 2048;
|
||||
S32 max_dimension = llmax(width, height); // Find largest dimension
|
||||
S32 block_area = MAX_BLOCK_SIZE * MAX_BLOCK_SIZE; // Calculated initial block area from established max block size (currently 64)
|
||||
block_area *= llmax((max_dimension / MAX_BLOCK_SIZE / max_components), 1); // Adjust initial block area by ratio of largest dimension to block size per component
|
||||
S32 totalbytes = (S32) (block_area * max_components * precision); // First block layer computed before loop without compression rate
|
||||
S32 block_layers = 1; // Start at layer 1 since first block layer is computed outside loop
|
||||
while (block_layers < 6) // Walk five layers for the five discards in JPEG2000
|
||||
S32 max_layers = (S32)llmax(llround(log2f((float)max_dimension) - log2f((float)MAX_BLOCK_SIZE)), 4); // Find number of powers of two between extents and block size to a minimum of 4
|
||||
block_area *= llmax(max_layers, 1); // Adjust initial block area by max number of layers
|
||||
S32 totalbytes = (S32) (MIN_LAYER_SIZE * max_components * precision); // Start estimation with a minimum reasonable size
|
||||
S32 block_layers = 0;
|
||||
while (block_layers <= max_layers) // Walk the layers
|
||||
{
|
||||
if (block_layers <= (5 - discard_level)) // Walk backwards from discard 5 to required discard layer.
|
||||
totalbytes += (S32) (block_area * max_components * precision * rate); // Add each block layer reduced by assumed compression rate
|
||||
|
|
|
|||
|
|
@ -32,8 +32,6 @@
|
|||
#include "event.h"
|
||||
#include "cio.h"
|
||||
|
||||
#define MAX_ENCODED_DISCARD_LEVELS 5
|
||||
|
||||
// Factory function: see declaration in llimagej2c.cpp
|
||||
LLImageJ2CImpl* fallbackCreateLLImageJ2CImpl()
|
||||
{
|
||||
|
|
@ -135,73 +133,96 @@ static void opj_error(const char* msg, void* user_data)
|
|||
|
||||
static OPJ_SIZE_T opj_read(void * buffer, OPJ_SIZE_T bytes, void* user_data)
|
||||
{
|
||||
llassert(user_data);
|
||||
llassert(user_data && buffer);
|
||||
|
||||
JPEG2KBase* jpeg_codec = static_cast<JPEG2KBase*>(user_data);
|
||||
OPJ_SIZE_T remainder = (jpeg_codec->size - jpeg_codec->offset);
|
||||
if (remainder <= 0)
|
||||
|
||||
if (jpeg_codec->offset < 0 || static_cast<OPJ_SIZE_T>(jpeg_codec->offset) >= jpeg_codec->size)
|
||||
{
|
||||
jpeg_codec->offset = jpeg_codec->size;
|
||||
// Indicate end of stream (hacky?)
|
||||
return (OPJ_OFF_T)-1;
|
||||
return static_cast<OPJ_SIZE_T>(-1); // Indicate EOF
|
||||
}
|
||||
OPJ_SIZE_T to_read = llclamp(U32(bytes), U32(0), U32(remainder));
|
||||
|
||||
OPJ_SIZE_T remainder = jpeg_codec->size - static_cast<OPJ_SIZE_T>(jpeg_codec->offset);
|
||||
OPJ_SIZE_T to_read = (bytes < remainder) ? bytes : remainder;
|
||||
|
||||
memcpy(buffer, jpeg_codec->buffer + jpeg_codec->offset, to_read);
|
||||
jpeg_codec->offset += to_read;
|
||||
|
||||
return to_read;
|
||||
}
|
||||
|
||||
static OPJ_SIZE_T opj_write(void * buffer, OPJ_SIZE_T bytes, void* user_data)
|
||||
{
|
||||
llassert(user_data);
|
||||
llassert(user_data && buffer);
|
||||
|
||||
JPEG2KBase* jpeg_codec = static_cast<JPEG2KBase*>(user_data);
|
||||
OPJ_SIZE_T remainder = jpeg_codec->size - jpeg_codec->offset;
|
||||
if (remainder < bytes)
|
||||
OPJ_OFF_T required_offset = jpeg_codec->offset + static_cast<OPJ_OFF_T>(bytes);
|
||||
|
||||
// Overflow check
|
||||
if (required_offset < jpeg_codec->offset)
|
||||
return 0; // Overflow detected
|
||||
|
||||
// Resize if needed (exponential growth)
|
||||
if (required_offset > static_cast<OPJ_OFF_T>(jpeg_codec->size))
|
||||
{
|
||||
OPJ_SIZE_T new_size = jpeg_codec->size + (bytes - remainder);
|
||||
OPJ_SIZE_T new_size = jpeg_codec->size ? jpeg_codec->size : 1024;
|
||||
while (required_offset > static_cast<OPJ_OFF_T>(new_size))
|
||||
new_size *= 2;
|
||||
|
||||
const OPJ_SIZE_T MAX_BUFFER_SIZE = 512 * 1024 * 1024; // 512 MB, increase if needed
|
||||
if (new_size > MAX_BUFFER_SIZE) return 0;
|
||||
|
||||
U8* new_buffer = (U8*)ll_aligned_malloc_16(new_size);
|
||||
memcpy(new_buffer, jpeg_codec->buffer, jpeg_codec->offset);
|
||||
U8* old_buffer = jpeg_codec->buffer;
|
||||
if (!new_buffer) return 0; // Allocation failed
|
||||
|
||||
if (jpeg_codec->offset > 0)
|
||||
memcpy(new_buffer, jpeg_codec->buffer, static_cast<size_t>(jpeg_codec->offset));
|
||||
|
||||
ll_aligned_free_16(jpeg_codec->buffer);
|
||||
jpeg_codec->buffer = new_buffer;
|
||||
ll_aligned_free_16(old_buffer);
|
||||
jpeg_codec->size = new_size;
|
||||
}
|
||||
memcpy(jpeg_codec->buffer + jpeg_codec->offset, buffer, bytes);
|
||||
jpeg_codec->offset += bytes;
|
||||
|
||||
memcpy(jpeg_codec->buffer + jpeg_codec->offset, buffer, static_cast<size_t>(bytes));
|
||||
jpeg_codec->offset = required_offset;
|
||||
return bytes;
|
||||
}
|
||||
|
||||
static OPJ_OFF_T opj_skip(OPJ_OFF_T bytes, void* user_data)
|
||||
{
|
||||
llassert(user_data);
|
||||
JPEG2KBase* jpeg_codec = static_cast<JPEG2KBase*>(user_data);
|
||||
jpeg_codec->offset += bytes;
|
||||
|
||||
if (jpeg_codec->offset > (OPJ_OFF_T)jpeg_codec->size)
|
||||
OPJ_OFF_T new_offset = jpeg_codec->offset + bytes;
|
||||
|
||||
if (new_offset < 0 || new_offset > static_cast<OPJ_OFF_T>(jpeg_codec->size))
|
||||
{
|
||||
jpeg_codec->offset = jpeg_codec->size;
|
||||
// Indicate end of stream
|
||||
return (OPJ_OFF_T)-1;
|
||||
}
|
||||
|
||||
if (jpeg_codec->offset < 0)
|
||||
{
|
||||
// Shouldn't be possible?
|
||||
jpeg_codec->offset = 0;
|
||||
// Clamp and indicate EOF or error
|
||||
jpeg_codec->offset = llclamp<OPJ_OFF_T>(new_offset, 0, static_cast<OPJ_OFF_T>(jpeg_codec->size));
|
||||
return (OPJ_OFF_T)-1;
|
||||
}
|
||||
|
||||
jpeg_codec->offset = new_offset;
|
||||
return bytes;
|
||||
}
|
||||
|
||||
static OPJ_BOOL opj_seek(OPJ_OFF_T bytes, void * user_data)
|
||||
static OPJ_BOOL opj_seek(OPJ_OFF_T offset, void * user_data)
|
||||
{
|
||||
llassert(user_data);
|
||||
JPEG2KBase* jpeg_codec = static_cast<JPEG2KBase*>(user_data);
|
||||
jpeg_codec->offset = bytes;
|
||||
jpeg_codec->offset = llclamp(U32(jpeg_codec->offset), U32(0), U32(jpeg_codec->size));
|
||||
|
||||
if (offset < 0 || offset > static_cast<OPJ_OFF_T>(jpeg_codec->size))
|
||||
return OPJ_FALSE;
|
||||
|
||||
jpeg_codec->offset = offset;
|
||||
return OPJ_TRUE;
|
||||
}
|
||||
|
||||
static void opj_free_user_data(void * user_data)
|
||||
{
|
||||
llassert(user_data);
|
||||
|
||||
JPEG2KBase* jpeg_codec = static_cast<JPEG2KBase*>(user_data);
|
||||
// Don't free, data is managed externally
|
||||
jpeg_codec->buffer = nullptr;
|
||||
|
|
@ -211,14 +232,54 @@ static void opj_free_user_data(void * user_data)
|
|||
|
||||
static void opj_free_user_data_write(void * user_data)
|
||||
{
|
||||
llassert(user_data);
|
||||
|
||||
JPEG2KBase* jpeg_codec = static_cast<JPEG2KBase*>(user_data);
|
||||
// Free, data was allocated here
|
||||
ll_aligned_free_16(jpeg_codec->buffer);
|
||||
jpeg_codec->buffer = nullptr;
|
||||
if (jpeg_codec->buffer)
|
||||
{
|
||||
ll_aligned_free_16(jpeg_codec->buffer);
|
||||
jpeg_codec->buffer = nullptr;
|
||||
}
|
||||
jpeg_codec->size = 0;
|
||||
jpeg_codec->offset = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Estimates the number of layers necessary depending on the image surface (w x h)
|
||||
*/
|
||||
static U32 estimate_num_layers(U32 surface)
|
||||
{
|
||||
if (surface <= 1024) return 2; // Tiny (≤32×32)
|
||||
else if (surface <= 16384) return 3; // Small (≤128×128)
|
||||
else if (surface <= 262144) return 4; // Medium (≤512×512)
|
||||
else if (surface <= 1048576) return 5; // Up to ~1MP
|
||||
else return 6; // Up to ~1.5–2MP
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the parameters.tcp_rates according to the number of layers and a last tcp_rate value (which equals to the final compression ratio).
|
||||
*
|
||||
* Example for 6 layers:
|
||||
*
|
||||
* i = 5, parameters.tcp_rates[6 - 1 - 5] = 8.0f * (1 << (5 << 1)) = 8192 // Layer 5 (lowest quality)
|
||||
* i = 4, parameters.tcp_rates[6 - 1 - 4] = 8.0f * (1 << (4 << 1)) = 2048 // Layer 4
|
||||
* i = 3, parameters.tcp_rates[6 - 1 - 3] = 8.0f * (1 << (3 << 1)) = 512 // Layer 3
|
||||
* i = 2, parameters.tcp_rates[6 - 1 - 2] = 8.0f * (1 << (2 << 1)) = 128 // Layer 2
|
||||
* i = 1, parameters.tcp_rates[6 - 1 - 1] = 8.0f * (1 << (1 << 1)) = 32 // Layer 1
|
||||
* i = 0, parameters.tcp_rates[6 - 1 - 0] = 8.0f * (1 << (0 << 1)) = 8 // Layer 0 (highest quality)
|
||||
*
|
||||
*/
|
||||
static void set_tcp_rates(opj_cparameters_t* parameters, U32 num_layers = 1, F32 last_tcp_rate = LAST_TCP_RATE)
|
||||
{
|
||||
parameters->tcp_numlayers = num_layers;
|
||||
|
||||
for (int i = num_layers - 1; i >= 0; i--)
|
||||
{
|
||||
parameters->tcp_rates[num_layers - 1 - i] = last_tcp_rate * static_cast<F32>(1 << (i << 1));
|
||||
}
|
||||
}
|
||||
|
||||
class JPEG2KDecode : public JPEG2KBase
|
||||
{
|
||||
public:
|
||||
|
|
@ -433,15 +494,16 @@ public:
|
|||
|
||||
opj_set_default_encoder_parameters(¶meters);
|
||||
parameters.cod_format = OPJ_CODEC_J2K;
|
||||
parameters.cp_disto_alloc = 1;
|
||||
parameters.prog_order = OPJ_RLCP; // should be the default, but, just in case
|
||||
parameters.cp_disto_alloc = 1; // enable rate allocation by distortion
|
||||
parameters.max_cs_size = 0; // do not cap max size because we're using tcp_rates and also irrelevant with lossless.
|
||||
|
||||
if (reversible)
|
||||
{
|
||||
parameters.max_cs_size = 0; // do not limit size for reversible compression
|
||||
parameters.irreversible = 0; // should be the default, but, just in case
|
||||
parameters.tcp_numlayers = 1;
|
||||
/* documentation seems to be wrong, should be 0.0f for lossless, not 1.0f
|
||||
see https://github.com/uclouvain/openjpeg/blob/39e8c50a2f9bdcf36810ee3d41bcbf1cc78968ae/src/lib/openjp2/j2k.c#L7755
|
||||
see https://github.com/uclouvain/openjpeg/blob/e7453e398b110891778d8da19209792c69ca7169/src/lib/openjp2/j2k.c#L7817
|
||||
*/
|
||||
parameters.tcp_rates[0] = 0.0f;
|
||||
}
|
||||
|
|
@ -496,53 +558,22 @@ public:
|
|||
|
||||
encoder = opj_create_compress(OPJ_CODEC_J2K);
|
||||
|
||||
parameters.tcp_mct = (image->numcomps >= 3) ? 1 : 0;
|
||||
parameters.cod_format = OPJ_CODEC_J2K;
|
||||
parameters.prog_order = OPJ_RLCP;
|
||||
parameters.cp_disto_alloc = 1;
|
||||
parameters.tcp_mct = (image->numcomps >= 3) ? 1 : 0; // no color transform for RGBA images
|
||||
|
||||
|
||||
// if not lossless compression, computes tcp_numlayers and max_cs_size depending on the image dimensions
|
||||
if( parameters.irreversible ) {
|
||||
if( parameters.irreversible )
|
||||
{
|
||||
|
||||
// computes a number of layers
|
||||
U32 surface = rawImageIn.getWidth() * rawImageIn.getHeight();
|
||||
U32 nb_layers = 1;
|
||||
U32 s = 64*64;
|
||||
while (surface > s)
|
||||
{
|
||||
nb_layers++;
|
||||
s *= 4;
|
||||
}
|
||||
nb_layers = llclamp(nb_layers, 1, 6);
|
||||
|
||||
parameters.tcp_numlayers = nb_layers;
|
||||
parameters.tcp_rates[nb_layers - 1] = (U32)(1.f / DEFAULT_COMPRESSION_RATE); // 1:8 by default
|
||||
// gets the necessary number of layers
|
||||
U32 nb_layers = estimate_num_layers(surface);
|
||||
|
||||
// for each subsequent layer, computes its rate and adds surface * numcomps * 1/rate to the max_cs_size
|
||||
U32 max_cs_size = (U32)(surface * image->numcomps * DEFAULT_COMPRESSION_RATE);
|
||||
U32 multiplier;
|
||||
for (int i = nb_layers - 2; i >= 0; i--)
|
||||
{
|
||||
if( i == nb_layers - 2 )
|
||||
{
|
||||
multiplier = 15;
|
||||
}
|
||||
else if( i == nb_layers - 3 )
|
||||
{
|
||||
multiplier = 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
multiplier = 2;
|
||||
}
|
||||
parameters.tcp_rates[i] = parameters.tcp_rates[i + 1] * multiplier;
|
||||
max_cs_size += (U32)(surface * image->numcomps * (1 / parameters.tcp_rates[i]));
|
||||
}
|
||||
// fills parameters.tcp_rates and updates parameters.tcp_numlayers
|
||||
set_tcp_rates(¶meters, nb_layers, LAST_TCP_RATE);
|
||||
|
||||
//ensure that we have at least a minimal size
|
||||
max_cs_size = llmax(max_cs_size, (U32)FIRST_PACKET_SIZE);
|
||||
|
||||
parameters.max_cs_size = max_cs_size;
|
||||
}
|
||||
|
||||
if (!opj_setup_encoder(encoder, ¶meters, image))
|
||||
|
|
@ -554,7 +585,20 @@ public:
|
|||
opj_set_warning_handler(encoder, opj_warn, this);
|
||||
opj_set_error_handler(encoder, opj_error, this);
|
||||
|
||||
U32 tile_count = (rawImageIn.getWidth() >> 6) * (rawImageIn.getHeight() >> 6);
|
||||
U32 width_tiles = (rawImageIn.getWidth() >> 6);
|
||||
U32 height_tiles = (rawImageIn.getHeight() >> 6);
|
||||
|
||||
// Allow images with a width or height that are MIN_IMAGE_SIZE <= x < 64
|
||||
if (width_tiles == 0 && (rawImageIn.getWidth() >= MIN_IMAGE_SIZE))
|
||||
{
|
||||
width_tiles = 1;
|
||||
}
|
||||
if (height_tiles == 0 && (rawImageIn.getHeight() >= MIN_IMAGE_SIZE))
|
||||
{
|
||||
height_tiles = 1;
|
||||
}
|
||||
|
||||
U32 tile_count = width_tiles * height_tiles;
|
||||
U32 data_size_guess = tile_count * TILE_SIZE;
|
||||
|
||||
// will be freed in opj_free_user_data_write
|
||||
|
|
@ -569,7 +613,7 @@ public:
|
|||
opj_stream_destroy(stream);
|
||||
}
|
||||
|
||||
stream = opj_stream_create(data_size_guess, false);
|
||||
stream = opj_stream_create(data_size_guess, OPJ_FALSE);
|
||||
if (!stream)
|
||||
{
|
||||
return false;
|
||||
|
|
@ -610,16 +654,15 @@ public:
|
|||
|
||||
void setImage(const LLImageRaw& raw)
|
||||
{
|
||||
opj_image_cmptparm_t cmptparm[MAX_ENCODED_DISCARD_LEVELS];
|
||||
memset(&cmptparm[0], 0, MAX_ENCODED_DISCARD_LEVELS * sizeof(opj_image_cmptparm_t));
|
||||
|
||||
S32 numcomps = raw.getComponents();
|
||||
S32 width = raw.getWidth();
|
||||
S32 height = raw.getHeight();
|
||||
S32 width = raw.getWidth();
|
||||
S32 height = raw.getHeight();
|
||||
|
||||
std::vector<opj_image_cmptparm_t> cmptparm(numcomps);
|
||||
|
||||
for (S32 c = 0; c < numcomps; c++)
|
||||
{
|
||||
cmptparm[c].prec = 8;
|
||||
cmptparm[c].prec = 8; // replaces .bpp
|
||||
cmptparm[c].sgnd = 0;
|
||||
cmptparm[c].dx = parameters.subsampling_dx;
|
||||
cmptparm[c].dy = parameters.subsampling_dy;
|
||||
|
|
@ -627,7 +670,7 @@ public:
|
|||
cmptparm[c].h = height;
|
||||
}
|
||||
|
||||
image = opj_image_create(numcomps, &cmptparm[0], OPJ_CLRSPC_SRGB);
|
||||
image = opj_image_create(numcomps, cmptparm.data(), OPJ_CLRSPC_SRGB);
|
||||
|
||||
image->x1 = width;
|
||||
image->y1 = height;
|
||||
|
|
@ -639,7 +682,7 @@ public:
|
|||
{
|
||||
for (S32 x = 0; x < width; x++)
|
||||
{
|
||||
const U8 *pixel = src_datap + (y*width + x) * numcomps;
|
||||
const U8 *pixel = src_datap + (y * width + x) * numcomps;
|
||||
for (S32 c = 0; c < numcomps; c++)
|
||||
{
|
||||
image->comps[c].data[i] = *pixel;
|
||||
|
|
@ -915,12 +958,11 @@ bool LLImageJ2COJ::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, con
|
|||
{
|
||||
JPEG2KEncode encode(comment_text, reversible);
|
||||
bool encoded = encode.encode(raw_image, base);
|
||||
if (encoded)
|
||||
if (!encoded)
|
||||
{
|
||||
LL_WARNS() << "Openjpeg encoding implementation isn't complete, returning false" << LL_ENDL;
|
||||
LL_WARNS() << "Openjpeg encoding was unsuccessful, returning false" << LL_ENDL;
|
||||
}
|
||||
return encoded;
|
||||
//return false;
|
||||
}
|
||||
|
||||
bool LLImageJ2COJ::getMetadata(LLImageJ2C &base)
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@
|
|||
|
||||
#include "llimagej2c.h"
|
||||
|
||||
const F32 LAST_TCP_RATE = 1.f/DEFAULT_COMPRESSION_RATE; // should be 8, giving a 1:8 ratio
|
||||
|
||||
class LLImageJ2COJ : public LLImageJ2CImpl
|
||||
{
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -37,98 +37,98 @@
|
|||
#include "llsettingsdaycycle.h"
|
||||
|
||||
// Grid out of which parcels taken is stepped every 4 meters.
|
||||
const F32 PARCEL_GRID_STEP_METERS = 4.f;
|
||||
constexpr F32 PARCEL_GRID_STEP_METERS = 4.f;
|
||||
|
||||
// Area of one "square" of parcel
|
||||
const S32 PARCEL_UNIT_AREA = 16;
|
||||
constexpr S32 PARCEL_UNIT_AREA = 16;
|
||||
|
||||
// Height _above_ground_ that parcel boundary ends
|
||||
const F32 PARCEL_HEIGHT = 50.f;
|
||||
constexpr F32 PARCEL_HEIGHT = 50.f;
|
||||
|
||||
//Height above ground which parcel boundries exist for explicitly banned avatars
|
||||
const F32 BAN_HEIGHT = 5000.f;
|
||||
constexpr F32 BAN_HEIGHT = 5000.f;
|
||||
|
||||
// Maximum number of entries in an access list
|
||||
const S32 PARCEL_MAX_ACCESS_LIST = 300;
|
||||
constexpr S32 PARCEL_MAX_ACCESS_LIST = 300;
|
||||
//Maximum number of entires in an update packet
|
||||
//for access/ban lists.
|
||||
const F32 PARCEL_MAX_ENTRIES_PER_PACKET = 48.f;
|
||||
constexpr F32 PARCEL_MAX_ENTRIES_PER_PACKET = 48.f;
|
||||
|
||||
// Maximum number of experiences
|
||||
const S32 PARCEL_MAX_EXPERIENCE_LIST = 24;
|
||||
constexpr S32 PARCEL_MAX_EXPERIENCE_LIST = 24;
|
||||
|
||||
// Weekly charge for listing a parcel in the directory
|
||||
const S32 PARCEL_DIRECTORY_FEE = 30;
|
||||
constexpr S32 PARCEL_DIRECTORY_FEE = 30;
|
||||
|
||||
const S32 PARCEL_PASS_PRICE_DEFAULT = 10;
|
||||
const F32 PARCEL_PASS_HOURS_DEFAULT = 1.f;
|
||||
constexpr S32 PARCEL_PASS_PRICE_DEFAULT = 10;
|
||||
constexpr F32 PARCEL_PASS_HOURS_DEFAULT = 1.f;
|
||||
|
||||
// Number of "chunks" in which parcel overlay data is sent
|
||||
// Chunk 0 = southern rows, entire width
|
||||
const S32 PARCEL_OVERLAY_CHUNKS = 4;
|
||||
constexpr S32 PARCEL_OVERLAY_CHUNKS = 4;
|
||||
|
||||
// Bottom three bits are a color index for the land overlay
|
||||
const U8 PARCEL_COLOR_MASK = 0x07;
|
||||
const U8 PARCEL_PUBLIC = 0x00;
|
||||
const U8 PARCEL_OWNED = 0x01;
|
||||
const U8 PARCEL_GROUP = 0x02;
|
||||
const U8 PARCEL_SELF = 0x03;
|
||||
const U8 PARCEL_FOR_SALE = 0x04;
|
||||
const U8 PARCEL_AUCTION = 0x05;
|
||||
constexpr U8 PARCEL_COLOR_MASK = 0x07;
|
||||
constexpr U8 PARCEL_PUBLIC = 0x00;
|
||||
constexpr U8 PARCEL_OWNED = 0x01;
|
||||
constexpr U8 PARCEL_GROUP = 0x02;
|
||||
constexpr U8 PARCEL_SELF = 0x03;
|
||||
constexpr U8 PARCEL_FOR_SALE = 0x04;
|
||||
constexpr U8 PARCEL_AUCTION = 0x05;
|
||||
// unused 0x06
|
||||
// unused 0x07
|
||||
// flag, unused 0x08
|
||||
const U8 PARCEL_HIDDENAVS = 0x10; // avatars not visible outside of parcel. Used for 'see avs' feature, but must be off for compatibility
|
||||
const U8 PARCEL_SOUND_LOCAL = 0x20;
|
||||
const U8 PARCEL_WEST_LINE = 0x40; // flag, property line on west edge
|
||||
const U8 PARCEL_SOUTH_LINE = 0x80; // flag, property line on south edge
|
||||
constexpr U8 PARCEL_HIDDENAVS = 0x10; // avatars not visible outside of parcel. Used for 'see avs' feature, but must be off for compatibility
|
||||
constexpr U8 PARCEL_SOUND_LOCAL = 0x20;
|
||||
constexpr U8 PARCEL_WEST_LINE = 0x40; // flag, property line on west edge
|
||||
constexpr U8 PARCEL_SOUTH_LINE = 0x80; // flag, property line on south edge
|
||||
|
||||
// Transmission results for parcel properties
|
||||
const S32 PARCEL_RESULT_NO_DATA = -1;
|
||||
const S32 PARCEL_RESULT_SUCCESS = 0; // got exactly one parcel
|
||||
const S32 PARCEL_RESULT_MULTIPLE = 1; // got multiple parcels
|
||||
constexpr S32 PARCEL_RESULT_NO_DATA = -1;
|
||||
constexpr S32 PARCEL_RESULT_SUCCESS = 0; // got exactly one parcel
|
||||
constexpr S32 PARCEL_RESULT_MULTIPLE = 1; // got multiple parcels
|
||||
|
||||
const S32 SELECTED_PARCEL_SEQ_ID = -10000;
|
||||
const S32 COLLISION_NOT_IN_GROUP_PARCEL_SEQ_ID = -20000;
|
||||
const S32 COLLISION_BANNED_PARCEL_SEQ_ID = -30000;
|
||||
const S32 COLLISION_NOT_ON_LIST_PARCEL_SEQ_ID = -40000;
|
||||
const S32 HOVERED_PARCEL_SEQ_ID = -50000;
|
||||
constexpr S32 SELECTED_PARCEL_SEQ_ID = -10000;
|
||||
constexpr S32 COLLISION_NOT_IN_GROUP_PARCEL_SEQ_ID = -20000;
|
||||
constexpr S32 COLLISION_BANNED_PARCEL_SEQ_ID = -30000;
|
||||
constexpr S32 COLLISION_NOT_ON_LIST_PARCEL_SEQ_ID = -40000;
|
||||
constexpr S32 HOVERED_PARCEL_SEQ_ID = -50000;
|
||||
|
||||
const U32 RT_NONE = 0x1 << 0;
|
||||
const U32 RT_OWNER = 0x1 << 1;
|
||||
const U32 RT_GROUP = 0x1 << 2;
|
||||
const U32 RT_OTHER = 0x1 << 3;
|
||||
const U32 RT_LIST = 0x1 << 4;
|
||||
const U32 RT_SELL = 0x1 << 5;
|
||||
constexpr U32 RT_NONE = 0x1 << 0;
|
||||
constexpr U32 RT_OWNER = 0x1 << 1;
|
||||
constexpr U32 RT_GROUP = 0x1 << 2;
|
||||
constexpr U32 RT_OTHER = 0x1 << 3;
|
||||
constexpr U32 RT_LIST = 0x1 << 4;
|
||||
constexpr U32 RT_SELL = 0x1 << 5;
|
||||
|
||||
const S32 INVALID_PARCEL_ID = -1;
|
||||
constexpr S32 INVALID_PARCEL_ID = -1;
|
||||
|
||||
const S32 INVALID_PARCEL_ENVIRONMENT_VERSION = -2;
|
||||
constexpr S32 INVALID_PARCEL_ENVIRONMENT_VERSION = -2;
|
||||
// if Region settings are used, parcel env. version is -1
|
||||
const S32 UNSET_PARCEL_ENVIRONMENT_VERSION = -1;
|
||||
constexpr S32 UNSET_PARCEL_ENVIRONMENT_VERSION = -1;
|
||||
|
||||
// Timeouts for parcels
|
||||
// default is 21 days * 24h/d * 60m/h * 60s/m *1000000 usec/s = 1814400000000
|
||||
const U64 DEFAULT_USEC_CONVERSION_TIMEOUT = U64L(1814400000000);
|
||||
constexpr U64 DEFAULT_USEC_CONVERSION_TIMEOUT = U64L(1814400000000);
|
||||
// ***** TESTING is 10 minutes
|
||||
//const U64 DEFAULT_USEC_CONVERSION_TIMEOUT = U64L(600000000);
|
||||
|
||||
// group is 60 days * 24h/d * 60m/h * 60s/m *1000000 usec/s = 5184000000000
|
||||
const U64 GROUP_USEC_CONVERSION_TIMEOUT = U64L(5184000000000);
|
||||
constexpr U64 GROUP_USEC_CONVERSION_TIMEOUT = U64L(5184000000000);
|
||||
// ***** TESTING is 10 minutes
|
||||
//const U64 GROUP_USEC_CONVERSION_TIMEOUT = U64L(600000000);
|
||||
|
||||
// default sale timeout is 2 days -> 172800000000
|
||||
const U64 DEFAULT_USEC_SALE_TIMEOUT = U64L(172800000000);
|
||||
constexpr U64 DEFAULT_USEC_SALE_TIMEOUT = U64L(172800000000);
|
||||
// ***** TESTING is 10 minutes
|
||||
//const U64 DEFAULT_USEC_SALE_TIMEOUT = U64L(600000000);
|
||||
|
||||
// more grace period extensions.
|
||||
const U64 SEVEN_DAYS_IN_USEC = U64L(604800000000);
|
||||
constexpr U64 SEVEN_DAYS_IN_USEC = U64L(604800000000);
|
||||
|
||||
// if more than 100,000s before sale revert, and no extra extension
|
||||
// has been given, go ahead and extend it more. That's about 1.2 days.
|
||||
const S32 EXTEND_GRACE_IF_MORE_THAN_SEC = 100000;
|
||||
constexpr S32 EXTEND_GRACE_IF_MORE_THAN_SEC = 100000;
|
||||
|
||||
|
||||
|
||||
|
|
@ -250,9 +250,9 @@ public:
|
|||
void setMediaURL(const std::string& url);
|
||||
void setMediaType(const std::string& type);
|
||||
void setMediaDesc(const std::string& desc);
|
||||
void setMediaID(const LLUUID& id) { mMediaID = id; }
|
||||
void setMediaAutoScale ( U8 flagIn ) { mMediaAutoScale = flagIn; }
|
||||
void setMediaLoop (U8 loop) { mMediaLoop = loop; }
|
||||
void setMediaID(const LLUUID& id) { mMediaID = id; }
|
||||
void setMediaAutoScale ( U8 flagIn ) { mMediaAutoScale = flagIn; }
|
||||
void setMediaLoop(U8 loop) { mMediaLoop = loop; }
|
||||
void setMediaWidth(S32 width);
|
||||
void setMediaHeight(S32 height);
|
||||
void setMediaCurrentURL(const std::string& url);
|
||||
|
|
|
|||
|
|
@ -661,7 +661,7 @@ void LLSettingsSky::blend(LLSettingsBase::ptr_t &end, F64 blendf)
|
|||
mHasLegacyHaze |= lerp_legacy_float(mHazeDensity, mLegacyHazeDensity, other->mHazeDensity, other->mLegacyHazeDensity, 0.7f, (F32)blendf);
|
||||
mHasLegacyHaze |= lerp_legacy_float(mDistanceMultiplier, mLegacyDistanceMultiplier, other->mDistanceMultiplier, other->mLegacyDistanceMultiplier, 0.8f, (F32)blendf);
|
||||
mHasLegacyHaze |= lerp_legacy_float(mDensityMultiplier, mLegacyDensityMultiplier, other->mDensityMultiplier, other->mLegacyDensityMultiplier, 0.0001f, (F32)blendf);
|
||||
mHasLegacyHaze |= lerp_legacy_color(mAmbientColor, mLegacyAmbientColor, other->mAmbientColor, other->mLegacyAmbientColor, LLColor3(0.25f, 0.25f, 0.25f), (F32)blendf); // <FS:Beq for Hecklezz/> import pending PR #4185 Fix sky ambient color not blending.
|
||||
mHasLegacyHaze |= lerp_legacy_color(mAmbientColor, mLegacyAmbientColor, other->mAmbientColor, other->mLegacyAmbientColor, LLColor3(0.25f, 0.25f, 0.25f), (F32)blendf);
|
||||
mHasLegacyHaze |= lerp_legacy_color(mBlueHorizon, mLegacyBlueHorizon, other->mBlueHorizon, other->mLegacyBlueHorizon, LLColor3(0.4954f, 0.4954f, 0.6399f), (F32)blendf);
|
||||
mHasLegacyHaze |= lerp_legacy_color(mBlueDensity, mLegacyBlueDensity, other->mBlueDensity, other->mLegacyBlueDensity, LLColor3(0.2447f, 0.4487f, 0.7599f), (F32)blendf);
|
||||
parammapping_t defaults = other->getParameterMap();
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@
|
|||
#include "linden_common.h"
|
||||
|
||||
#include "math.h"
|
||||
//#include "vmath.h"
|
||||
#include "v3math.h"
|
||||
#include "llquaternion.h"
|
||||
#include "m3math.h"
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@
|
|||
#include "linden_common.h"
|
||||
|
||||
#include "llmath.h"
|
||||
//#include "vmath.h"
|
||||
#include "v3math.h"
|
||||
#include "patch_dct.h"
|
||||
#include "patch_code.h"
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@
|
|||
#include "linden_common.h"
|
||||
|
||||
#include "llmath.h"
|
||||
//#include "vmath.h"
|
||||
#include "v3math.h"
|
||||
#include "patch_dct.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@
|
|||
#include "linden_common.h"
|
||||
|
||||
#include "llmath.h"
|
||||
//#include "vmath.h"
|
||||
#include "v3math.h"
|
||||
#include "patch_dct.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -594,7 +594,7 @@ void LLPluginProcessParent::idle(void)
|
|||
params.args.add("-e");
|
||||
params.args.add("tell application \"Terminal\"");
|
||||
params.args.add("-e");
|
||||
params.args.add(STRINGIZE("set win to do script \"lldb -pid "
|
||||
params.args.add(STRINGIZE("set win to do script \"lldb -p "
|
||||
<< mProcess->getProcessID() << "\""));
|
||||
params.args.add("-e");
|
||||
params.args.add("do script \"continue\" in win");
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ include(ColladaDom)
|
|||
|
||||
set(llprimitive_SOURCE_FILES
|
||||
lldaeloader.cpp
|
||||
llgltfloader.cpp
|
||||
llgltfmaterial.cpp
|
||||
llmaterialid.cpp
|
||||
llmaterial.cpp
|
||||
|
|
@ -33,7 +32,6 @@ set(llprimitive_SOURCE_FILES
|
|||
set(llprimitive_HEADER_FILES
|
||||
CMakeLists.txt
|
||||
lldaeloader.h
|
||||
llgltfloader.h
|
||||
llgltfmaterial.h
|
||||
llgltfmaterial_templates.h
|
||||
legacy_object_types.h
|
||||
|
|
|
|||
|
|
@ -241,12 +241,15 @@ LLModel::EModelStatus load_face_from_dom_triangles(
|
|||
|
||||
if (idx_stride <= 0
|
||||
|| (pos_source && pos_offset >= idx_stride)
|
||||
|| (pos_source && pos_offset < 0)
|
||||
|| (tc_source && tc_offset >= idx_stride)
|
||||
|| (norm_source && norm_offset >= idx_stride))
|
||||
|| (tc_source && tc_offset < 0)
|
||||
|| (norm_source && norm_offset >= idx_stride)
|
||||
|| (norm_source && norm_offset < 0))
|
||||
{
|
||||
// Looks like these offsets should fit inside idx_stride
|
||||
// Might be good idea to also check idx.getCount()%idx_stride != 0
|
||||
LL_WARNS() << "Invalid pos_offset " << pos_offset << ", tc_offset " << tc_offset << " or norm_offset " << norm_offset << LL_ENDL;
|
||||
LL_WARNS() << "Invalid idx_stride " << idx_stride << ", pos_offset " << pos_offset << ", tc_offset " << tc_offset << " or norm_offset " << norm_offset << LL_ENDL;
|
||||
return LLModel::BAD_ELEMENT;
|
||||
}
|
||||
|
||||
|
|
@ -920,6 +923,7 @@ LLDAELoader::LLDAELoader(
|
|||
std::map<std::string, std::string, std::less<>>& jointAliasMap,
|
||||
U32 maxJointsPerMesh,
|
||||
U32 modelLimit,
|
||||
U32 debugMode,
|
||||
// <FS:Beq> mesh loader suffix configuration
|
||||
// bool preprocess)
|
||||
bool preprocess,
|
||||
|
|
@ -936,8 +940,9 @@ LLDAELoader::LLDAELoader(
|
|||
jointTransformMap,
|
||||
jointsFromNodes,
|
||||
jointAliasMap,
|
||||
maxJointsPerMesh),
|
||||
mGeneratedModelLimit(modelLimit),
|
||||
maxJointsPerMesh,
|
||||
modelLimit,
|
||||
debugMode),
|
||||
mPreprocessDAE(preprocess)
|
||||
{
|
||||
// <FS:Beq> mesh loader suffix configuration
|
||||
|
|
@ -1753,6 +1758,7 @@ void LLDAELoader::processDomModel(LLModel* model, DAE* dae, daeElement* root, do
|
|||
{
|
||||
materials[model->mMaterialList[i]] = LLImportMaterial();
|
||||
}
|
||||
// todo: likely a bug here, shouldn't be using suffixed label, see how it gets used in other places.
|
||||
mScene[transformation].push_back(LLModelInstance(model, model->mLabel, transformation, materials));
|
||||
stretch_extents(model, transformation);
|
||||
}
|
||||
|
|
@ -2495,7 +2501,7 @@ std::string LLDAELoader::getElementLabel(daeElement *element)
|
|||
}
|
||||
|
||||
// static
|
||||
size_t LLDAELoader::getSuffixPosition(std::string label)
|
||||
size_t LLDAELoader::getSuffixPosition(const std::string &label)
|
||||
{
|
||||
// <FS:Beq> Selectable suffixes
|
||||
//if ((label.find("_LOD") != -1) || (label.find("_PHYS") != -1))
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ public:
|
|||
std::map<std::string, std::string, std::less<>>& jointAliasMap,
|
||||
U32 maxJointsPerMesh,
|
||||
U32 modelLimit,
|
||||
U32 debugMode,
|
||||
// <FS:Beq> configrable lod suffix support
|
||||
// bool preprocess);
|
||||
bool preprocess,
|
||||
|
|
@ -103,14 +104,13 @@ protected:
|
|||
bool loadModelsFromDomMesh(domMesh* mesh, std::vector<LLModel*>& models_out, U32 submodel_limit);
|
||||
|
||||
static std::string getElementLabel(daeElement *element);
|
||||
static size_t getSuffixPosition(std::string label);
|
||||
static size_t getSuffixPosition(const std::string& label);
|
||||
static std::string getLodlessLabel(daeElement *element);
|
||||
static LODSuffixArray sLODSuffix; // <FS:Beq/> mesh loader suffix configuration
|
||||
public: // <FS:Beq/> open up for local mesh to use
|
||||
static std::string preprocessDAE(std::string filename);
|
||||
|
||||
private:
|
||||
U32 mGeneratedModelLimit; // Attempt to limit amount of generated submodels
|
||||
bool mPreprocessDAE;
|
||||
};
|
||||
#endif // LL_LLDAELLOADER_H
|
||||
|
|
|
|||
|
|
@ -1,404 +0,0 @@
|
|||
/**
|
||||
* @file LLGLTFLoader.cpp
|
||||
* @brief LLGLTFLoader class implementation
|
||||
*
|
||||
* $LicenseInfo:firstyear=2022&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2022, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#include "llgltfloader.h"
|
||||
|
||||
// Import & define single-header gltf import/export lib
|
||||
#define TINYGLTF_IMPLEMENTATION
|
||||
#define TINYGLTF_USE_CPP14 // default is C++ 11
|
||||
|
||||
// tinygltf by default loads image files using STB
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
// to use our own image loading:
|
||||
// 1. replace this definition with TINYGLTF_NO_STB_IMAGE
|
||||
// 2. provide image loader callback with TinyGLTF::SetImageLoader(LoadimageDataFunction LoadImageData, void *user_data)
|
||||
|
||||
// tinygltf saves image files using STB
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
// similarly, can override with TINYGLTF_NO_STB_IMAGE_WRITE and TinyGLTF::SetImageWriter(fxn, data)
|
||||
|
||||
// Additionally, disable inclusion of STB header files entirely with
|
||||
// TINYGLTF_NO_INCLUDE_STB_IMAGE
|
||||
// TINYGLTF_NO_INCLUDE_STB_IMAGE_WRITE
|
||||
#include "tinygltf/tiny_gltf.h"
|
||||
|
||||
|
||||
// TODO: includes inherited from dae loader. Validate / prune
|
||||
|
||||
#include "llsdserialize.h"
|
||||
#include "lljoint.h"
|
||||
|
||||
#include "llmatrix4a.h"
|
||||
|
||||
#include <boost/regex.hpp>
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
|
||||
static const std::string lod_suffix[LLModel::NUM_LODS] =
|
||||
{
|
||||
"_LOD0",
|
||||
"_LOD1",
|
||||
"_LOD2",
|
||||
"",
|
||||
"_PHYS",
|
||||
};
|
||||
|
||||
|
||||
LLGLTFLoader::LLGLTFLoader(std::string filename,
|
||||
S32 lod,
|
||||
LLModelLoader::load_callback_t load_cb,
|
||||
LLModelLoader::joint_lookup_func_t joint_lookup_func,
|
||||
LLModelLoader::texture_load_func_t texture_load_func,
|
||||
LLModelLoader::state_callback_t state_cb,
|
||||
void * opaque_userdata,
|
||||
JointTransformMap & jointTransformMap,
|
||||
JointNameSet & jointsFromNodes,
|
||||
std::map<std::string, std::string, std::less<>> & jointAliasMap,
|
||||
U32 maxJointsPerMesh,
|
||||
U32 modelLimit) //,
|
||||
//bool preprocess)
|
||||
: LLModelLoader( filename,
|
||||
lod,
|
||||
load_cb,
|
||||
joint_lookup_func,
|
||||
texture_load_func,
|
||||
state_cb,
|
||||
opaque_userdata,
|
||||
jointTransformMap,
|
||||
jointsFromNodes,
|
||||
jointAliasMap,
|
||||
maxJointsPerMesh ),
|
||||
//mPreprocessGLTF(preprocess),
|
||||
mMeshesLoaded(false),
|
||||
mMaterialsLoaded(false)
|
||||
{
|
||||
}
|
||||
|
||||
LLGLTFLoader::~LLGLTFLoader() {}
|
||||
|
||||
bool LLGLTFLoader::OpenFile(const std::string &filename)
|
||||
{
|
||||
tinygltf::TinyGLTF loader;
|
||||
std::string error_msg;
|
||||
std::string warn_msg;
|
||||
std::string filename_lc(filename);
|
||||
LLStringUtil::toLower(filename_lc);
|
||||
|
||||
// Load a tinygltf model fom a file. Assumes that the input filename has already been
|
||||
// been sanitized to one of (.gltf , .glb) extensions, so does a simple find to distinguish.
|
||||
if (std::string::npos == filename_lc.rfind(".gltf"))
|
||||
{ // file is binary
|
||||
mGltfLoaded = loader.LoadBinaryFromFile(&mGltfModel, &error_msg, &warn_msg, filename);
|
||||
}
|
||||
else
|
||||
{ // file is ascii
|
||||
mGltfLoaded = loader.LoadASCIIFromFile(&mGltfModel, &error_msg, &warn_msg, filename);
|
||||
}
|
||||
|
||||
if (!mGltfLoaded)
|
||||
{
|
||||
if (!warn_msg.empty())
|
||||
LL_WARNS("GLTF_IMPORT") << "gltf load warning: " << warn_msg.c_str() << LL_ENDL;
|
||||
if (!error_msg.empty())
|
||||
LL_WARNS("GLTF_IMPORT") << "gltf load error: " << error_msg.c_str() << LL_ENDL;
|
||||
return false;
|
||||
}
|
||||
|
||||
mMeshesLoaded = parseMeshes();
|
||||
if (mMeshesLoaded) uploadMeshes();
|
||||
|
||||
mMaterialsLoaded = parseMaterials();
|
||||
if (mMaterialsLoaded) uploadMaterials();
|
||||
|
||||
return (mMeshesLoaded || mMaterialsLoaded);
|
||||
}
|
||||
|
||||
bool LLGLTFLoader::parseMeshes()
|
||||
{
|
||||
if (!mGltfLoaded) return false;
|
||||
|
||||
// 2022-04 DJH Volume params from dae example. TODO understand PCODE
|
||||
LLVolumeParams volume_params;
|
||||
volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE);
|
||||
|
||||
for (tinygltf::Mesh mesh : mGltfModel.meshes)
|
||||
{
|
||||
LLModel *pModel = new LLModel(volume_params, 0.f);
|
||||
|
||||
if (populateModelFromMesh(pModel, mesh) &&
|
||||
(LLModel::NO_ERRORS == pModel->getStatus()) &&
|
||||
validate_model(pModel))
|
||||
{
|
||||
mModelList.push_back(pModel);
|
||||
}
|
||||
else
|
||||
{
|
||||
setLoadState(ERROR_MODEL + pModel->getStatus());
|
||||
delete(pModel);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LLGLTFLoader::populateModelFromMesh(LLModel* pModel, const tinygltf::Mesh &mesh)
|
||||
{
|
||||
pModel->mLabel = mesh.name;
|
||||
int pos_idx;
|
||||
tinygltf::Accessor indices_a, positions_a, normals_a, uv0_a, color0_a;
|
||||
|
||||
auto prims = mesh.primitives;
|
||||
for (auto prim : prims)
|
||||
{
|
||||
if (prim.indices >= 0) indices_a = mGltfModel.accessors[prim.indices];
|
||||
|
||||
pos_idx = (prim.attributes.count("POSITION") > 0) ? prim.attributes.at("POSITION") : -1;
|
||||
if (pos_idx >= 0)
|
||||
{
|
||||
positions_a = mGltfModel.accessors[pos_idx];
|
||||
if (TINYGLTF_COMPONENT_TYPE_FLOAT != positions_a.componentType)
|
||||
continue;
|
||||
auto positions_bv = mGltfModel.bufferViews[positions_a.bufferView];
|
||||
auto positions_buf = mGltfModel.buffers[positions_bv.buffer];
|
||||
//auto type = positions_vb.
|
||||
//if (positions_buf.name
|
||||
}
|
||||
|
||||
#if 0
|
||||
int norm_idx, tan_idx, uv0_idx, uv1_idx, color0_idx, color1_idx;
|
||||
norm_idx = (prim.attributes.count("NORMAL") > 0) ? prim.attributes.at("NORMAL") : -1;
|
||||
tan_idx = (prim.attributes.count("TANGENT") > 0) ? prim.attributes.at("TANGENT") : -1;
|
||||
uv0_idx = (prim.attributes.count("TEXCOORDS_0") > 0) ? prim.attributes.at("TEXCOORDS_0") : -1;
|
||||
uv1_idx = (prim.attributes.count("TEXCOORDS_1") > 0) ? prim.attributes.at("TEXCOORDS_1") : -1;
|
||||
color0_idx = (prim.attributes.count("COLOR_0") > 0) ? prim.attributes.at("COLOR_0") : -1;
|
||||
color1_idx = (prim.attributes.count("COLOR_1") > 0) ? prim.attributes.at("COLOR_1") : -1;
|
||||
#endif
|
||||
|
||||
if (prim.mode == TINYGLTF_MODE_TRIANGLES)
|
||||
{
|
||||
//auto pos = mesh. TODO resume here DJH 2022-04
|
||||
}
|
||||
}
|
||||
|
||||
//pModel->addFace()
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LLGLTFLoader::parseMaterials()
|
||||
{
|
||||
if (!mGltfLoaded) return false;
|
||||
|
||||
// fill local texture data structures
|
||||
mSamplers.clear();
|
||||
for (auto in_sampler : mGltfModel.samplers)
|
||||
{
|
||||
gltf_sampler sampler;
|
||||
sampler.magFilter = in_sampler.magFilter > 0 ? in_sampler.magFilter : GL_LINEAR;
|
||||
sampler.minFilter = in_sampler.minFilter > 0 ? in_sampler.minFilter : GL_LINEAR;;
|
||||
sampler.wrapS = in_sampler.wrapS;
|
||||
sampler.wrapT = in_sampler.wrapT;
|
||||
sampler.name = in_sampler.name; // unused
|
||||
mSamplers.push_back(sampler);
|
||||
}
|
||||
|
||||
mImages.clear();
|
||||
for (auto in_image : mGltfModel.images)
|
||||
{
|
||||
gltf_image image;
|
||||
image.numChannels = in_image.component;
|
||||
image.bytesPerChannel = in_image.bits >> 3; // Convert bits to bytes
|
||||
image.pixelType = in_image.pixel_type; // Maps exactly, i.e. TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE == GL_UNSIGNED_BYTE, etc
|
||||
image.size = static_cast<U32>(in_image.image.size());
|
||||
image.height = in_image.height;
|
||||
image.width = in_image.width;
|
||||
image.data = in_image.image.data();
|
||||
|
||||
if (in_image.as_is)
|
||||
{
|
||||
LL_WARNS("GLTF_IMPORT") << "Unsupported image encoding" << LL_ENDL;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (image.size != image.height * image.width * image.numChannels * image.bytesPerChannel)
|
||||
{
|
||||
LL_WARNS("GLTF_IMPORT") << "Image size error" << LL_ENDL;
|
||||
return false;
|
||||
}
|
||||
|
||||
mImages.push_back(image);
|
||||
}
|
||||
|
||||
mTextures.clear();
|
||||
for (auto in_tex : mGltfModel.textures)
|
||||
{
|
||||
gltf_texture tex;
|
||||
tex.imageIdx = in_tex.source;
|
||||
tex.samplerIdx = in_tex.sampler;
|
||||
tex.imageUuid.setNull();
|
||||
|
||||
if (tex.imageIdx >= mImages.size() || tex.samplerIdx >= mSamplers.size())
|
||||
{
|
||||
LL_WARNS("GLTF_IMPORT") << "Texture sampler/image index error" << LL_ENDL;
|
||||
return false;
|
||||
}
|
||||
|
||||
mTextures.push_back(tex);
|
||||
}
|
||||
|
||||
// parse each material
|
||||
for (tinygltf::Material gltf_material : mGltfModel.materials)
|
||||
{
|
||||
gltf_render_material mat;
|
||||
mat.name = gltf_material.name;
|
||||
|
||||
tinygltf::PbrMetallicRoughness& pbr = gltf_material.pbrMetallicRoughness;
|
||||
mat.hasPBR = true; // Always true, for now
|
||||
|
||||
mat.baseColor.set(pbr.baseColorFactor.data());
|
||||
mat.hasBaseTex = pbr.baseColorTexture.index >= 0;
|
||||
mat.baseColorTexIdx = pbr.baseColorTexture.index;
|
||||
mat.baseColorTexCoords = pbr.baseColorTexture.texCoord;
|
||||
|
||||
mat.metalness = pbr.metallicFactor;
|
||||
mat.roughness = pbr.roughnessFactor;
|
||||
mat.hasMRTex = pbr.metallicRoughnessTexture.index >= 0;
|
||||
mat.metalRoughTexIdx = pbr.metallicRoughnessTexture.index;
|
||||
mat.metalRoughTexCoords = pbr.metallicRoughnessTexture.texCoord;
|
||||
|
||||
mat.normalScale = gltf_material.normalTexture.scale;
|
||||
mat.hasNormalTex = gltf_material.normalTexture.index >= 0;
|
||||
mat.normalTexIdx = gltf_material.normalTexture.index;
|
||||
mat.normalTexCoords = gltf_material.normalTexture.texCoord;
|
||||
|
||||
mat.occlusionScale = gltf_material.occlusionTexture.strength;
|
||||
mat.hasOcclusionTex = gltf_material.occlusionTexture.index >= 0;
|
||||
mat.occlusionTexIdx = gltf_material.occlusionTexture.index;
|
||||
mat.occlusionTexCoords = gltf_material.occlusionTexture.texCoord;
|
||||
|
||||
mat.emissiveColor.set(gltf_material.emissiveFactor.data());
|
||||
mat.hasEmissiveTex = gltf_material.emissiveTexture.index >= 0;
|
||||
mat.emissiveTexIdx = gltf_material.emissiveTexture.index;
|
||||
mat.emissiveTexCoords = gltf_material.emissiveTexture.texCoord;
|
||||
|
||||
mat.alphaMode = gltf_material.alphaMode;
|
||||
mat.alphaMask = gltf_material.alphaCutoff;
|
||||
|
||||
if ((mat.hasNormalTex && (mat.normalTexIdx >= mTextures.size())) ||
|
||||
(mat.hasOcclusionTex && (mat.occlusionTexIdx >= mTextures.size())) ||
|
||||
(mat.hasEmissiveTex && (mat.emissiveTexIdx >= mTextures.size())) ||
|
||||
(mat.hasBaseTex && (mat.baseColorTexIdx >= mTextures.size())) ||
|
||||
(mat.hasMRTex && (mat.metalRoughTexIdx >= mTextures.size())))
|
||||
{
|
||||
LL_WARNS("GLTF_IMPORT") << "Texture resource index error" << LL_ENDL;
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((mat.hasNormalTex && (mat.normalTexCoords > 2)) || // mesh can have up to 3 sets of UV
|
||||
(mat.hasOcclusionTex && (mat.occlusionTexCoords > 2)) ||
|
||||
(mat.hasEmissiveTex && (mat.emissiveTexCoords > 2)) ||
|
||||
(mat.hasBaseTex && (mat.baseColorTexCoords > 2)) ||
|
||||
(mat.hasMRTex && (mat.metalRoughTexCoords > 2)))
|
||||
{
|
||||
LL_WARNS("GLTF_IMPORT") << "Image texcoord index error" << LL_ENDL;
|
||||
return false;
|
||||
}
|
||||
|
||||
mMaterials.push_back(mat);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: convert raw vertex buffers to UUIDs
|
||||
void LLGLTFLoader::uploadMeshes()
|
||||
{
|
||||
llassert(0);
|
||||
}
|
||||
|
||||
// convert raw image buffers to texture UUIDs & assemble into a render material
|
||||
void LLGLTFLoader::uploadMaterials()
|
||||
{
|
||||
for (gltf_render_material mat : mMaterials) // Initially 1 material per gltf file, but design for multiple
|
||||
{
|
||||
if (mat.hasBaseTex)
|
||||
{
|
||||
gltf_texture& gtex = mTextures[mat.baseColorTexIdx];
|
||||
if (gtex.imageUuid.isNull())
|
||||
{
|
||||
gtex.imageUuid = imageBufferToTextureUUID(gtex);
|
||||
}
|
||||
}
|
||||
|
||||
if (mat.hasMRTex)
|
||||
{
|
||||
gltf_texture& gtex = mTextures[mat.metalRoughTexIdx];
|
||||
if (gtex.imageUuid.isNull())
|
||||
{
|
||||
gtex.imageUuid = imageBufferToTextureUUID(gtex);
|
||||
}
|
||||
}
|
||||
|
||||
if (mat.hasNormalTex)
|
||||
{
|
||||
gltf_texture& gtex = mTextures[mat.normalTexIdx];
|
||||
if (gtex.imageUuid.isNull())
|
||||
{
|
||||
gtex.imageUuid = imageBufferToTextureUUID(gtex);
|
||||
}
|
||||
}
|
||||
|
||||
if (mat.hasOcclusionTex)
|
||||
{
|
||||
gltf_texture& gtex = mTextures[mat.occlusionTexIdx];
|
||||
if (gtex.imageUuid.isNull())
|
||||
{
|
||||
gtex.imageUuid = imageBufferToTextureUUID(gtex);
|
||||
}
|
||||
}
|
||||
|
||||
if (mat.hasEmissiveTex)
|
||||
{
|
||||
gltf_texture& gtex = mTextures[mat.emissiveTexIdx];
|
||||
if (gtex.imageUuid.isNull())
|
||||
{
|
||||
gtex.imageUuid = imageBufferToTextureUUID(gtex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LLUUID LLGLTFLoader::imageBufferToTextureUUID(const gltf_texture& tex)
|
||||
{
|
||||
//gltf_image& image = mImages[tex.imageIdx];
|
||||
//gltf_sampler& sampler = mSamplers[tex.samplerIdx];
|
||||
|
||||
// fill an LLSD container with image+sampler data
|
||||
|
||||
// upload texture
|
||||
|
||||
// retrieve UUID
|
||||
|
||||
return LLUUID::null;
|
||||
}
|
||||
|
|
@ -1,206 +0,0 @@
|
|||
/**
|
||||
* @file LLGLTFLoader.h
|
||||
* @brief LLGLTFLoader class definition
|
||||
*
|
||||
* $LicenseInfo:firstyear=2022&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2022, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#ifndef LL_LLGLTFLoader_H
|
||||
#define LL_LLGLTFLoader_H
|
||||
|
||||
#include "tinygltf/tiny_gltf.h"
|
||||
|
||||
#include "llglheaders.h"
|
||||
#include "llmodelloader.h"
|
||||
|
||||
// gltf_* structs are temporary, used to organize the subset of data that eventually goes into the material LLSD
|
||||
|
||||
class gltf_sampler
|
||||
{
|
||||
public:
|
||||
// Uses GL enums
|
||||
S32 minFilter; // GL_NEAREST, GL_LINEAR, GL_NEAREST_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_NEAREST, GL_NEAREST_MIPMAP_LINEAR or GL_LINEAR_MIPMAP_LINEAR
|
||||
S32 magFilter; // GL_NEAREST or GL_LINEAR
|
||||
S32 wrapS; // GL_CLAMP_TO_EDGE, GL_MIRRORED_REPEAT or GL_REPEAT
|
||||
S32 wrapT; // GL_CLAMP_TO_EDGE, GL_MIRRORED_REPEAT or GL_REPEAT
|
||||
//S32 wrapR; // Found in some sample files, but not part of glTF 2.0 spec. Ignored.
|
||||
std::string name; // optional, currently unused
|
||||
// extensions and extras are sampler optional fields that we don't support - at least initially
|
||||
};
|
||||
|
||||
class gltf_image
|
||||
{
|
||||
public:// Note that glTF images are defined with row 0 at the top (opposite of OpenGL)
|
||||
U8* data; // ptr to decoded image data
|
||||
U32 size; // in bytes, regardless of channel width
|
||||
U32 width;
|
||||
U32 height;
|
||||
U32 numChannels; // range 1..4
|
||||
U32 bytesPerChannel; // converted from gltf "bits", expects only 8, 16 or 32 as input
|
||||
U32 pixelType; // one of (TINYGLTF_COMPONENT_TYPE)_UNSIGNED_BYTE, _UNSIGNED_SHORT, _UNSIGNED_INT, or _FLOAT
|
||||
};
|
||||
|
||||
class gltf_texture
|
||||
{
|
||||
public:
|
||||
U32 imageIdx;
|
||||
U32 samplerIdx;
|
||||
LLUUID imageUuid = LLUUID::null;
|
||||
};
|
||||
|
||||
class gltf_render_material
|
||||
{
|
||||
public:
|
||||
std::string name;
|
||||
|
||||
// scalar values
|
||||
LLColor4 baseColor; // linear encoding. Multiplied with vertex color, if present.
|
||||
double metalness;
|
||||
double roughness;
|
||||
double normalScale; // scale applies only to X,Y components of normal
|
||||
double occlusionScale; // strength multiplier for occlusion
|
||||
LLColor4 emissiveColor; // emissive mulitiplier, assumed linear encoding (spec 2.0 is silent)
|
||||
std::string alphaMode; // "OPAQUE", "MASK" or "BLEND"
|
||||
double alphaMask; // alpha cut-off
|
||||
|
||||
// textures
|
||||
U32 baseColorTexIdx; // always sRGB encoded
|
||||
U32 metalRoughTexIdx; // always linear, roughness in G channel, metalness in B channel
|
||||
U32 normalTexIdx; // linear, valid range R[0-1], G[0-1], B[0.5-1]. Normal = texel * 2 - vec3(1.0)
|
||||
U32 occlusionTexIdx; // linear, occlusion in R channel, 0 meaning fully occluded, 1 meaning not occluded
|
||||
U32 emissiveTexIdx; // always stored as sRGB, in nits (candela / meter^2)
|
||||
|
||||
// texture coordinates
|
||||
U32 baseColorTexCoords;
|
||||
U32 metalRoughTexCoords;
|
||||
U32 normalTexCoords;
|
||||
U32 occlusionTexCoords;
|
||||
U32 emissiveTexCoords;
|
||||
|
||||
// TODO: Add traditional (diffuse, normal, specular) UUIDs here, or add this struct to LL_TextureEntry??
|
||||
|
||||
bool hasPBR;
|
||||
bool hasBaseTex, hasMRTex, hasNormalTex, hasOcclusionTex, hasEmissiveTex;
|
||||
|
||||
// This field is populated after upload
|
||||
LLUUID material_uuid = LLUUID::null;
|
||||
|
||||
};
|
||||
|
||||
class gltf_mesh
|
||||
{
|
||||
public:
|
||||
std::string name;
|
||||
|
||||
// TODO add mesh import DJH 2022-04
|
||||
|
||||
};
|
||||
|
||||
class LLGLTFLoader : public LLModelLoader
|
||||
{
|
||||
public:
|
||||
typedef std::map<std::string, LLImportMaterial> material_map;
|
||||
|
||||
LLGLTFLoader(std::string filename,
|
||||
S32 lod,
|
||||
LLModelLoader::load_callback_t load_cb,
|
||||
LLModelLoader::joint_lookup_func_t joint_lookup_func,
|
||||
LLModelLoader::texture_load_func_t texture_load_func,
|
||||
LLModelLoader::state_callback_t state_cb,
|
||||
void * opaque_userdata,
|
||||
JointTransformMap & jointTransformMap,
|
||||
JointNameSet & jointsFromNodes,
|
||||
std::map<std::string, std::string,std::less<>> &jointAliasMap,
|
||||
U32 maxJointsPerMesh,
|
||||
U32 modelLimit); //,
|
||||
//bool preprocess );
|
||||
virtual ~LLGLTFLoader();
|
||||
|
||||
virtual bool OpenFile(const std::string &filename);
|
||||
|
||||
protected:
|
||||
tinygltf::Model mGltfModel;
|
||||
bool mGltfLoaded;
|
||||
bool mMeshesLoaded;
|
||||
bool mMaterialsLoaded;
|
||||
|
||||
std::vector<gltf_mesh> mMeshes;
|
||||
std::vector<gltf_render_material> mMaterials;
|
||||
|
||||
std::vector<gltf_texture> mTextures;
|
||||
std::vector<gltf_image> mImages;
|
||||
std::vector<gltf_sampler> mSamplers;
|
||||
|
||||
private:
|
||||
bool parseMeshes();
|
||||
void uploadMeshes();
|
||||
bool parseMaterials();
|
||||
void uploadMaterials();
|
||||
bool populateModelFromMesh(LLModel* pModel, const tinygltf::Mesh &mesh);
|
||||
LLUUID imageBufferToTextureUUID(const gltf_texture& tex);
|
||||
|
||||
// bool mPreprocessGLTF;
|
||||
|
||||
/* Below inherited from dae loader - unknown if/how useful here
|
||||
|
||||
void processElement(gltfElement *element, bool &badElement, GLTF *gltf);
|
||||
void processGltfModel(LLModel *model, GLTF *gltf, gltfElement *pRoot, gltfMesh *mesh, gltfSkin *skin);
|
||||
|
||||
material_map getMaterials(LLModel *model, gltfInstance_geometry *instance_geo, GLTF *gltf);
|
||||
LLImportMaterial profileToMaterial(gltfProfile_COMMON *material, GLTF *gltf);
|
||||
LLColor4 getGltfColor(gltfElement *element);
|
||||
|
||||
gltfElement *getChildFromElement(gltfElement *pElement, std::string const &name);
|
||||
|
||||
bool isNodeAJoint(gltfNode *pNode);
|
||||
void processJointNode(gltfNode *pNode, std::map<std::string, LLMatrix4> &jointTransforms);
|
||||
void extractTranslation(gltfTranslate *pTranslate, LLMatrix4 &transform);
|
||||
void extractTranslationViaElement(gltfElement *pTranslateElement, LLMatrix4 &transform);
|
||||
void extractTranslationViaSID(gltfElement *pElement, LLMatrix4 &transform);
|
||||
void buildJointToNodeMappingFromScene(gltfElement *pRoot);
|
||||
void processJointToNodeMapping(gltfNode *pNode);
|
||||
void processChildJoints(gltfNode *pParentNode);
|
||||
|
||||
bool verifyCount(int expected, int result);
|
||||
|
||||
// Verify that a controller matches vertex counts
|
||||
bool verifyController(gltfController *pController);
|
||||
|
||||
static bool addVolumeFacesFromGltfMesh(LLModel *model, gltfMesh *mesh, LLSD &log_msg);
|
||||
static bool createVolumeFacesFromGltfMesh(LLModel *model, gltfMesh *mesh);
|
||||
|
||||
static LLModel *loadModelFromGltfMesh(gltfMesh *mesh);
|
||||
|
||||
// Loads a mesh breaking it into one or more models as necessary
|
||||
// to get around volume face limitations while retaining >8 materials
|
||||
//
|
||||
bool loadModelsFromGltfMesh(gltfMesh *mesh, std::vector<LLModel *> &models_out, U32 submodel_limit);
|
||||
|
||||
static std::string getElementLabel(gltfElement *element);
|
||||
static size_t getSuffixPosition(std::string label);
|
||||
static std::string getLodlessLabel(gltfElement *element);
|
||||
|
||||
static std::string preprocessGLTF(std::string filename);
|
||||
*/
|
||||
|
||||
};
|
||||
#endif // LL_LLGLTFLLOADER_H
|
||||
|
|
@ -346,6 +346,162 @@ void LLModel::normalizeVolumeFaces()
|
|||
}
|
||||
}
|
||||
|
||||
void LLModel::normalizeVolumeFacesAndWeights()
|
||||
{
|
||||
if (!mVolumeFaces.empty())
|
||||
{
|
||||
LLVector4a min, max;
|
||||
|
||||
// For all of the volume faces
|
||||
// in the model, loop over
|
||||
// them and see what the extents
|
||||
// of the volume along each axis.
|
||||
min = mVolumeFaces[0].mExtents[0];
|
||||
max = mVolumeFaces[0].mExtents[1];
|
||||
|
||||
for (U32 i = 1; i < mVolumeFaces.size(); ++i)
|
||||
{
|
||||
LLVolumeFace& face = mVolumeFaces[i];
|
||||
|
||||
update_min_max(min, max, face.mExtents[0]);
|
||||
update_min_max(min, max, face.mExtents[1]);
|
||||
|
||||
if (face.mTexCoords)
|
||||
{
|
||||
LLVector2& min_tc = face.mTexCoordExtents[0];
|
||||
LLVector2& max_tc = face.mTexCoordExtents[1];
|
||||
|
||||
min_tc = face.mTexCoords[0];
|
||||
max_tc = face.mTexCoords[0];
|
||||
|
||||
for (S32 j = 1; j < face.mNumVertices; ++j)
|
||||
{
|
||||
update_min_max(min_tc, max_tc, face.mTexCoords[j]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
face.mTexCoordExtents[0].set(0, 0);
|
||||
face.mTexCoordExtents[1].set(1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Now that we have the extents of the model
|
||||
// we can compute the offset needed to center
|
||||
// the model at the origin.
|
||||
|
||||
// Compute center of the model
|
||||
// and make it negative to get translation
|
||||
// needed to center at origin.
|
||||
LLVector4a trans;
|
||||
trans.setAdd(min, max);
|
||||
trans.mul(-0.5f);
|
||||
|
||||
// Compute the total size along all
|
||||
// axes of the model.
|
||||
LLVector4a size;
|
||||
size.setSub(max, min);
|
||||
|
||||
// Prevent division by zero.
|
||||
F32 x = size[0];
|
||||
F32 y = size[1];
|
||||
F32 z = size[2];
|
||||
F32 w = size[3];
|
||||
if (fabs(x) < F_APPROXIMATELY_ZERO)
|
||||
{
|
||||
x = 1.0;
|
||||
}
|
||||
if (fabs(y) < F_APPROXIMATELY_ZERO)
|
||||
{
|
||||
y = 1.0;
|
||||
}
|
||||
if (fabs(z) < F_APPROXIMATELY_ZERO)
|
||||
{
|
||||
z = 1.0;
|
||||
}
|
||||
size.set(x, y, z, w);
|
||||
|
||||
// Compute scale as reciprocal of size
|
||||
LLVector4a scale;
|
||||
scale.splat(1.f);
|
||||
scale.div(size);
|
||||
|
||||
LLVector4a inv_scale(1.f);
|
||||
inv_scale.div(scale);
|
||||
|
||||
for (U32 i = 0; i < mVolumeFaces.size(); ++i)
|
||||
{
|
||||
LLVolumeFace& face = mVolumeFaces[i];
|
||||
|
||||
// We shrink the extents so
|
||||
// that they fall within
|
||||
// the unit cube.
|
||||
// VFExtents change
|
||||
face.mExtents[0].add(trans);
|
||||
face.mExtents[0].mul(scale);
|
||||
|
||||
face.mExtents[1].add(trans);
|
||||
face.mExtents[1].mul(scale);
|
||||
|
||||
// For all the positions, we scale
|
||||
// the positions to fit within the unit cube.
|
||||
LLVector4a* pos = (LLVector4a*)face.mPositions;
|
||||
LLVector4a* norm = (LLVector4a*)face.mNormals;
|
||||
LLVector4a* t = (LLVector4a*)face.mTangents;
|
||||
|
||||
for (S32 j = 0; j < face.mNumVertices; ++j)
|
||||
{
|
||||
pos[j].add(trans);
|
||||
pos[j].mul(scale);
|
||||
if (norm && !norm[j].equals3(LLVector4a::getZero()))
|
||||
{
|
||||
norm[j].mul(inv_scale);
|
||||
norm[j].normalize3();
|
||||
}
|
||||
|
||||
if (t)
|
||||
{
|
||||
F32 w = t[j].getF32ptr()[3];
|
||||
t[j].mul(inv_scale);
|
||||
t[j].normalize3();
|
||||
t[j].getF32ptr()[3] = w;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
weight_map old_weights = mSkinWeights;
|
||||
mSkinWeights.clear();
|
||||
mPosition.clear();
|
||||
|
||||
for (auto& weights : old_weights)
|
||||
{
|
||||
LLVector4a pos(weights.first.mV[VX], weights.first.mV[VY], weights.first.mV[VZ]);
|
||||
pos.add(trans);
|
||||
pos.mul(scale);
|
||||
LLVector3 scaled_pos(pos.getF32ptr());
|
||||
mPosition.push_back(scaled_pos);
|
||||
mSkinWeights[scaled_pos] = weights.second;
|
||||
}
|
||||
|
||||
// mNormalizedScale is the scale at which
|
||||
// we would need to multiply the model
|
||||
// by to get the original size of the
|
||||
// model instead of the normalized size.
|
||||
LLVector4a normalized_scale;
|
||||
normalized_scale.splat(1.f);
|
||||
normalized_scale.div(scale);
|
||||
mNormalizedScale.set(normalized_scale.getF32ptr());
|
||||
mNormalizedTranslation.set(trans.getF32ptr());
|
||||
mNormalizedTranslation *= -1.f;
|
||||
|
||||
// remember normalized scale so original dimensions can be recovered for mesh processing (i.e. tangent generation)
|
||||
for (auto& face : mVolumeFaces)
|
||||
{
|
||||
face.mNormalizedScale = mNormalizedScale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LLModel::getNormalizedScaleTranslation(LLVector3& scale_out, LLVector3& translation_out) const
|
||||
{
|
||||
scale_out = mNormalizedScale;
|
||||
|
|
@ -677,7 +833,7 @@ LLSD LLModel::writeModel(
|
|||
bool upload_skin,
|
||||
bool upload_joints,
|
||||
bool lock_scale_if_joint_position,
|
||||
bool nowrite,
|
||||
EWriteModelMode write_mode,
|
||||
bool as_slm,
|
||||
int submodel_id)
|
||||
{
|
||||
|
|
@ -956,10 +1112,10 @@ LLSD LLModel::writeModel(
|
|||
}
|
||||
}
|
||||
|
||||
return writeModelToStream(ostr, mdl, nowrite, as_slm);
|
||||
return writeModelToStream(ostr, mdl, write_mode, as_slm);
|
||||
}
|
||||
|
||||
LLSD LLModel::writeModelToStream(std::ostream& ostr, LLSD& mdl, bool nowrite, bool as_slm)
|
||||
LLSD LLModel::writeModelToStream(std::ostream& ostr, LLSD& mdl, EWriteModelMode write_mode, bool as_slm)
|
||||
{
|
||||
std::string::size_type cur_offset = 0;
|
||||
|
||||
|
|
@ -1021,7 +1177,11 @@ LLSD LLModel::writeModelToStream(std::ostream& ostr, LLSD& mdl, bool nowrite, bo
|
|||
}
|
||||
}
|
||||
|
||||
if (!nowrite)
|
||||
if (write_mode == WRITE_HUMAN)
|
||||
{
|
||||
ostr << mdl;
|
||||
}
|
||||
else if (write_mode == WRITE_BINARY)
|
||||
{
|
||||
LLSDSerialize::toBinary(header, ostr);
|
||||
|
||||
|
|
@ -1588,11 +1748,21 @@ LLSD LLMeshSkinInfo::asLLSD(bool include_joints, bool lock_scale_if_joint_positi
|
|||
{
|
||||
ret[ "joint_names" ][ i ] = mJointNames[ i ];
|
||||
if (mInvBindMatrix.size() < i) break; // <FS:Beq/> FIRE-34811 Crash during import due to missing inv_bind_matrices.
|
||||
// For model to work at all there must be a matching bind matrix,
|
||||
// so supply an indentity one if it isn't true
|
||||
// Note: can build an actual bind matrix from joints
|
||||
const LLMatrix4a& inv_bind = mInvBindMatrix.size() > i ? mInvBindMatrix[i] : LLMatrix4a::identity();
|
||||
if (i >= mInvBindMatrix.size())
|
||||
{
|
||||
LL_WARNS("MESHSKININFO") << "Joint index " << i << " (" << mJointNames[i] << ") exceeds inverse bind matrix size "
|
||||
<< mInvBindMatrix.size() << LL_ENDL;
|
||||
}
|
||||
|
||||
for (U32 j = 0; j < 4; j++)
|
||||
{
|
||||
for (U32 k = 0; k < 4; k++)
|
||||
{
|
||||
ret["inverse_bind_matrix"][i][j*4+k] = mInvBindMatrix[i].mMatrix[j][k];
|
||||
ret["inverse_bind_matrix"][i][j * 4 + k] = inv_bind.mMatrix[j][k];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1605,15 +1775,25 @@ LLSD LLMeshSkinInfo::asLLSD(bool include_joints, bool lock_scale_if_joint_positi
|
|||
}
|
||||
}
|
||||
|
||||
if ( include_joints && mAlternateBindMatrix.size() > 0 )
|
||||
// optional 'joint overrides'
|
||||
if (include_joints && mAlternateBindMatrix.size() > 0)
|
||||
{
|
||||
for (U32 i = 0; i < mJointNames.size(); ++i)
|
||||
{
|
||||
// If there is not enough to match mJointNames,
|
||||
// either supply no alternate matrixes at all or supply
|
||||
// replacements
|
||||
const LLMatrix4a& alt_bind = mAlternateBindMatrix.size() > i ? mAlternateBindMatrix[i] : LLMatrix4a::identity();
|
||||
if (i >= mAlternateBindMatrix.size())
|
||||
{
|
||||
LL_WARNS("MESHSKININFO") << "Joint index " << i << " (" << mJointNames[i] << ") exceeds alternate bind matrix size "
|
||||
<< mAlternateBindMatrix.size() << LL_ENDL;
|
||||
}
|
||||
for (U32 j = 0; j < 4; j++)
|
||||
{
|
||||
for (U32 k = 0; k < 4; k++)
|
||||
{
|
||||
ret["alt_inverse_bind_matrix"][i][j*4+k] = mAlternateBindMatrix[i].mMatrix[j][k];
|
||||
ret["alt_inverse_bind_matrix"][i][j * 4 + k] = alt_bind.mMatrix[j][k];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -162,6 +162,12 @@ public:
|
|||
bool loadSkinInfo(LLSD& header, std::istream& is);
|
||||
bool loadDecomposition(LLSD& header, std::istream& is);
|
||||
|
||||
enum EWriteModelMode
|
||||
{
|
||||
WRITE_NO = 0,
|
||||
WRITE_BINARY,
|
||||
WRITE_HUMAN,
|
||||
};
|
||||
static LLSD writeModel(
|
||||
std::ostream& ostr,
|
||||
LLModel* physics,
|
||||
|
|
@ -173,14 +179,14 @@ public:
|
|||
bool upload_skin,
|
||||
bool upload_joints,
|
||||
bool lock_scale_if_joint_position,
|
||||
bool nowrite = false,
|
||||
EWriteModelMode write_mode = WRITE_BINARY,
|
||||
bool as_slm = false,
|
||||
int submodel_id = 0);
|
||||
|
||||
static LLSD writeModelToStream(
|
||||
std::ostream& ostr,
|
||||
LLSD& mdl,
|
||||
bool nowrite = false, bool as_slm = false);
|
||||
EWriteModelMode write_mode = WRITE_BINARY, bool as_slm = false);
|
||||
|
||||
void ClearFacesAndMaterials() { mVolumeFaces.clear(); mMaterialList.clear(); }
|
||||
|
||||
|
|
@ -204,6 +210,7 @@ public:
|
|||
|
||||
void sortVolumeFacesByMaterialName();
|
||||
void normalizeVolumeFaces();
|
||||
void normalizeVolumeFacesAndWeights();
|
||||
void trimVolumeFacesToSize(U32 new_count = LL_SCULPT_MESH_MAX_FACES, LLVolume::face_list_t* remainder = NULL);
|
||||
void remapVolumeFaces();
|
||||
void optimizeVolumeFaces();
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@
|
|||
|
||||
#include "llmatrix4a.h"
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/exception/diagnostic_information.hpp>
|
||||
|
||||
#include "../llxml/llcontrol.h"
|
||||
|
||||
|
|
@ -115,7 +116,9 @@ LLModelLoader::LLModelLoader(
|
|||
JointTransformMap& jointTransformMap,
|
||||
JointNameSet& jointsFromNodes,
|
||||
JointMap& legalJointNamesMap,
|
||||
U32 maxJointsPerMesh)
|
||||
U32 maxJointsPerMesh,
|
||||
U32 modelLimit,
|
||||
U32 debugMode)
|
||||
: mJointList( jointTransformMap )
|
||||
, mJointsFromNode( jointsFromNodes )
|
||||
, LLThread("Model Loader")
|
||||
|
|
@ -123,7 +126,6 @@ LLModelLoader::LLModelLoader(
|
|||
, mLod(lod)
|
||||
, mTrySLM(false)
|
||||
, mFirstTransform(true)
|
||||
, mNumOfFetchingTextures(0)
|
||||
, mLoadCallback(load_cb)
|
||||
, mJointLookupFunc(joint_lookup_func)
|
||||
, mTextureLoadFunc(texture_load_func)
|
||||
|
|
@ -134,7 +136,10 @@ LLModelLoader::LLModelLoader(
|
|||
, mNoNormalize(false)
|
||||
, mNoOptimize(false)
|
||||
, mCacheOnlyHitIfRigged(false)
|
||||
, mTexturesNeedScaling(false)
|
||||
, mMaxJointsPerMesh(maxJointsPerMesh)
|
||||
, mGeneratedModelLimit(modelLimit)
|
||||
, mDebugMode(debugMode)
|
||||
, mJointMap(legalJointNamesMap)
|
||||
{
|
||||
assert_main_thread();
|
||||
|
|
@ -151,7 +156,44 @@ LLModelLoader::~LLModelLoader()
|
|||
void LLModelLoader::run()
|
||||
{
|
||||
mWarningsArray.clear();
|
||||
doLoadModel();
|
||||
try
|
||||
{
|
||||
doLoadModel();
|
||||
}
|
||||
// Model loader isn't mission critical, so we just log all exceptions
|
||||
catch (const LLException& e)
|
||||
{
|
||||
LL_WARNS("THREAD") << "LLException in model loader: " << e.what() << "" << LL_ENDL;
|
||||
LLSD args;
|
||||
args["Message"] = "UnknownException";
|
||||
args["FILENAME"] = mFilename;
|
||||
args["EXCEPTION"] = e.what();
|
||||
mWarningsArray.append(args);
|
||||
setLoadState(ERROR_PARSING);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
LL_WARNS() << "Exception in LLModelLoader::run: " << e.what() << LL_ENDL;
|
||||
LLSD args;
|
||||
args["Message"] = "UnknownException";
|
||||
args["FILENAME"] = mFilename;
|
||||
args["EXCEPTION"] = e.what();
|
||||
mWarningsArray.append(args);
|
||||
setLoadState(ERROR_PARSING);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG_UNHANDLED_EXCEPTION("LLModelLoader");
|
||||
LLSD args;
|
||||
args["Message"] = "UnknownException";
|
||||
args["FILENAME"] = mFilename;
|
||||
args["EXCEPTION"] = boost::current_exception_diagnostic_information();
|
||||
mWarningsArray.append(args);
|
||||
setLoadState(ERROR_PARSING);
|
||||
}
|
||||
|
||||
// todo: we are inside of a thread, push this into main thread worker,
|
||||
// not into doOnIdleOneTime that laks tread safety
|
||||
doOnIdleOneTime(boost::bind(&LLModelLoader::loadModelCallback,this));
|
||||
}
|
||||
|
||||
|
|
@ -203,7 +245,9 @@ bool LLModelLoader::doLoadModel()
|
|||
}
|
||||
}
|
||||
|
||||
return OpenFile(mFilename);
|
||||
bool res = OpenFile(mFilename);
|
||||
dumpDebugData(); // conditional on mDebugMode
|
||||
return res;
|
||||
}
|
||||
|
||||
void LLModelLoader::setLoadState(U32 state)
|
||||
|
|
@ -471,6 +515,148 @@ bool LLModelLoader::isRigSuitableForJointPositionUpload( const std::vector<std::
|
|||
return true;
|
||||
}
|
||||
|
||||
void LLModelLoader::dumpDebugData()
|
||||
{
|
||||
if (mDebugMode == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::string log_file = mFilename + "_importer.txt";
|
||||
LLStringUtil::toLower(log_file);
|
||||
llofstream file;
|
||||
file.open(log_file.c_str());
|
||||
if (!file)
|
||||
{
|
||||
LL_WARNS() << "dumpDebugData failed to open file " << log_file << LL_ENDL;
|
||||
return;
|
||||
}
|
||||
file << "Importing: " << mFilename << "\n";
|
||||
|
||||
std::map<std::string, LLMatrix4a> inv_bind;
|
||||
std::map<std::string, LLMatrix4a> alt_bind;
|
||||
for (LLPointer<LLModel>& mdl : mModelList)
|
||||
{
|
||||
|
||||
file << "Model name: " << mdl->mLabel << "\n";
|
||||
const LLMeshSkinInfo& skin_info = mdl->mSkinInfo;
|
||||
file << "Shape Bind matrix: " << skin_info.mBindShapeMatrix << "\n";
|
||||
file << "Skin Weights count: " << (S32)mdl->mSkinWeights.size() << "\n";
|
||||
|
||||
// some objects might have individual bind matrices,
|
||||
// but for now it isn't accounted for
|
||||
size_t joint_count = skin_info.mJointNames.size();
|
||||
for (size_t i = 0; i< joint_count;i++)
|
||||
{
|
||||
const std::string& joint = skin_info.mJointNames[i];
|
||||
if (skin_info.mInvBindMatrix.size() > i)
|
||||
{
|
||||
inv_bind[joint] = skin_info.mInvBindMatrix[i];
|
||||
}
|
||||
if (skin_info.mAlternateBindMatrix.size() > i)
|
||||
{
|
||||
alt_bind[joint] = skin_info.mAlternateBindMatrix[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
file << "\nInv Bind matrices.\n";
|
||||
for (auto& bind : inv_bind)
|
||||
{
|
||||
file << "Joint: " << bind.first << " Matrix: " << bind.second << "\n";
|
||||
}
|
||||
|
||||
file << "\nAlt Bind matrices.\n";
|
||||
for (auto& bind : alt_bind)
|
||||
{
|
||||
file << "Joint: " << bind.first << " Matrix: " << bind.second << "\n";
|
||||
}
|
||||
|
||||
if (mDebugMode == 2)
|
||||
{
|
||||
S32 model_count = 0;
|
||||
for (LLPointer<LLModel>& mdl : mModelList)
|
||||
{
|
||||
const LLVolume::face_list_t &face_list = mdl->getVolumeFaces();
|
||||
for (S32 face = 0; face < face_list.size(); face++)
|
||||
{
|
||||
const LLVolumeFace& vf = face_list[face];
|
||||
file << "\nModel: " << mdl->mLabel
|
||||
<< " face " << face
|
||||
<< " has " << vf.mNumVertices
|
||||
<< " vertices and " << vf.mNumIndices
|
||||
<< " indices " << "\n";
|
||||
|
||||
file << "\nPositions for model: " << mdl->mLabel << " face " << face << "\n";
|
||||
|
||||
for (S32 pos = 0; pos < vf.mNumVertices; ++pos)
|
||||
{
|
||||
file << vf.mPositions[pos] << " ";
|
||||
}
|
||||
|
||||
file << "\n\nIndices for model: " << mdl->mLabel << " face " << face << "\n";
|
||||
|
||||
for (S32 ind = 0; ind < vf.mNumIndices; ++ind)
|
||||
{
|
||||
file << vf.mIndices[ind] << " ";
|
||||
}
|
||||
}
|
||||
|
||||
file << "\n\nWeights for model: " << mdl->mLabel;
|
||||
for (auto& weights : mdl->mSkinWeights)
|
||||
{
|
||||
file << "\nVertex: " << weights.first << " Weights: ";
|
||||
for (auto& weight : weights.second)
|
||||
{
|
||||
file << weight.mJointIdx << ":" << weight.mWeight << " ";
|
||||
}
|
||||
}
|
||||
|
||||
file << "\n";
|
||||
model_count++;
|
||||
if (model_count == 5)
|
||||
{
|
||||
file << "Too many models, stopping at 5.\n";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (mDebugMode > 2)
|
||||
{
|
||||
file << "\nModel LLSDs\n";
|
||||
S32 model_count = 0;
|
||||
// some files contain too many models, so stop at 5.
|
||||
for (LLPointer<LLModel>& mdl : mModelList)
|
||||
{
|
||||
const LLMeshSkinInfo& skin_info = mdl->mSkinInfo;
|
||||
size_t joint_count = skin_info.mJointNames.size();
|
||||
size_t alt_count = skin_info.mAlternateBindMatrix.size();
|
||||
|
||||
LLModel::writeModel(
|
||||
file,
|
||||
nullptr,
|
||||
mdl,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
mdl->mPhysics,
|
||||
joint_count > 0,
|
||||
alt_count > 0,
|
||||
false,
|
||||
LLModel::WRITE_HUMAN,
|
||||
false,
|
||||
mdl->mSubmodelID);
|
||||
|
||||
file << "\n";
|
||||
model_count++;
|
||||
if (model_count == 5)
|
||||
{
|
||||
file << "Too many models, stopping at 5.\n";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//called in the main thread
|
||||
void LLModelLoader::loadTextures()
|
||||
|
|
@ -489,7 +675,7 @@ void LLModelLoader::loadTextures()
|
|||
|
||||
if(!material.mDiffuseMapFilename.empty())
|
||||
{
|
||||
mNumOfFetchingTextures += mTextureLoadFunc(material, mOpaqueData);
|
||||
mTextureLoadFunc(material, mOpaqueData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -111,8 +111,10 @@ public:
|
|||
|
||||
bool mTrySLM;
|
||||
bool mCacheOnlyHitIfRigged; // ignore cached SLM if it does not contain rig info (and we want rig info)
|
||||
bool mTexturesNeedScaling;
|
||||
|
||||
model_list mModelList;
|
||||
// The scene is pretty much what ends up getting loaded for upload. Basically assign things to this guy if you want something uploaded.
|
||||
scene mScene;
|
||||
|
||||
typedef std::queue<LLPointer<LLModel> > model_queue;
|
||||
|
|
@ -121,10 +123,16 @@ public:
|
|||
model_queue mPhysicsQ;
|
||||
|
||||
//map of avatar joints as named in COLLADA assets to internal joint names
|
||||
// Do not use this for anything other than looking up the name of a joint. This is populated elsewhere.
|
||||
JointMap mJointMap;
|
||||
|
||||
// The joint list is what you want to use to actually setup the specific joint transformations.
|
||||
JointTransformMap& mJointList;
|
||||
JointNameSet& mJointsFromNode;
|
||||
|
||||
|
||||
U32 mMaxJointsPerMesh;
|
||||
U32 mDebugMode; // see dumDebugData() for details
|
||||
|
||||
LLModelLoader(
|
||||
std::string filename,
|
||||
|
|
@ -137,7 +145,9 @@ public:
|
|||
JointTransformMap& jointTransformMap,
|
||||
JointNameSet& jointsFromNodes,
|
||||
JointMap& legalJointNamesMap,
|
||||
U32 maxJointsPerMesh);
|
||||
U32 maxJointsPerMesh,
|
||||
U32 modelLimit,
|
||||
U32 debugMode);
|
||||
virtual ~LLModelLoader();
|
||||
|
||||
virtual void setNoNormalize() { mNoNormalize = true; }
|
||||
|
|
@ -163,9 +173,6 @@ public:
|
|||
|
||||
void stretch_extents(const LLModel* model, const LLMatrix4& mat);
|
||||
|
||||
S32 mNumOfFetchingTextures ; // updated in the main thread
|
||||
bool areTexturesReady() { return !mNumOfFetchingTextures; } // called in the main thread.
|
||||
|
||||
bool verifyCount( int expected, int result );
|
||||
|
||||
//Determines the viability of an asset to be used as an avatar rig (w or w/o joint upload caps)
|
||||
|
|
@ -194,6 +201,7 @@ public:
|
|||
|
||||
const LLSD logOut() const { return mWarningsArray; }
|
||||
void clearLog() { mWarningsArray.clear(); }
|
||||
void dumpDebugData();
|
||||
|
||||
protected:
|
||||
LLModelLoader::load_callback_t mLoadCallback;
|
||||
|
|
@ -204,6 +212,7 @@ protected:
|
|||
|
||||
bool mRigValidJointUpload;
|
||||
U32 mLegacyRigFlags;
|
||||
U32 mGeneratedModelLimit; // Attempt to limit amount of generated submodels
|
||||
|
||||
bool mNoNormalize;
|
||||
bool mNoOptimize;
|
||||
|
|
|
|||
|
|
@ -1875,8 +1875,17 @@ bool LLImageGL::readBackRaw(S32 discard_level, LLImageRaw* imageraw, bool compre
|
|||
glGetTexLevelParameteriv(mTarget, gl_discard, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, (GLint*)&glbytes);
|
||||
if(!imageraw->allocateDataSize(width, height, ncomponents, glbytes))
|
||||
{
|
||||
LL_WARNS() << "Memory allocation failed for reading back texture. Size is: " << glbytes << LL_ENDL ;
|
||||
LL_WARNS() << "width: " << width << "height: " << height << "components: " << ncomponents << LL_ENDL ;
|
||||
constexpr S64 MAX_GL_BYTES = 2048 * 2048;
|
||||
if (glbytes > 0 && glbytes <= MAX_GL_BYTES)
|
||||
{
|
||||
LLError::LLUserWarningMsg::showOutOfMemory();
|
||||
LL_ERRS() << "Memory allocation failed for reading back texture. Data size: " << glbytes << LL_ENDL;
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS() << "Memory allocation failed for reading back texture. Data size is: " << glbytes << LL_ENDL;
|
||||
LL_WARNS() << "width: " << width << "height: " << height << "components: " << ncomponents << LL_ENDL;
|
||||
}
|
||||
return false ;
|
||||
}
|
||||
|
||||
|
|
@ -1887,8 +1896,18 @@ bool LLImageGL::readBackRaw(S32 discard_level, LLImageRaw* imageraw, bool compre
|
|||
{
|
||||
if(!imageraw->allocateDataSize(width, height, ncomponents))
|
||||
{
|
||||
LL_WARNS() << "Memory allocation failed for reading back texture." << LL_ENDL ;
|
||||
LL_WARNS() << "width: " << width << "height: " << height << "components: " << ncomponents << LL_ENDL ;
|
||||
constexpr F32 MAX_IMAGE_SIZE = 2048 * 2048;
|
||||
F32 size = (F32)width * (F32)height * (F32)ncomponents;
|
||||
if (size > 0 && size <= MAX_IMAGE_SIZE)
|
||||
{
|
||||
LLError::LLUserWarningMsg::showOutOfMemory();
|
||||
LL_ERRS() << "Memory allocation failed for reading back texture. Data size: " << size << LL_ENDL;
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS() << "Memory allocation failed for reading back texture." << LL_ENDL;
|
||||
LL_WARNS() << "width: " << width << "height: " << height << "components: " << ncomponents << LL_ENDL;
|
||||
}
|
||||
return false ;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -498,20 +498,22 @@ void LLRenderTarget::bindTexture(U32 index, S32 channel, LLTexUnit::eTextureFilt
|
|||
{
|
||||
gGL.getTexUnit(channel)->bindManual(mUsage, getTexture(index), filter_options == LLTexUnit::TFO_TRILINEAR || filter_options == LLTexUnit::TFO_ANISOTROPIC);
|
||||
|
||||
bool isSRGB = false;
|
||||
llassert(mInternalFormat.size() > index);
|
||||
switch (mInternalFormat[index])
|
||||
{
|
||||
case GL_SRGB:
|
||||
case GL_SRGB8:
|
||||
case GL_SRGB_ALPHA:
|
||||
case GL_SRGB8_ALPHA8:
|
||||
isSRGB = true;
|
||||
break;
|
||||
// <FS:Beq> Unused variable warning fix (Macos) none of this code is needed.
|
||||
// bool isSRGB = false;
|
||||
// llassert(mInternalFormat.size() > index);
|
||||
// switch (mInternalFormat[index])
|
||||
// {
|
||||
// case GL_SRGB:
|
||||
// case GL_SRGB8:
|
||||
// case GL_SRGB_ALPHA:
|
||||
// case GL_SRGB8_ALPHA8:
|
||||
// isSRGB = true;
|
||||
// break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// default:
|
||||
// break;
|
||||
// }
|
||||
// </FS:Beq>
|
||||
|
||||
gGL.getTexUnit(channel)->setTextureFilteringOption(filter_options);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1116,7 +1116,7 @@ void LLScrollListCtrl::deleteItems(const LLSD& sd)
|
|||
void LLScrollListCtrl::deleteSelectedItems()
|
||||
{
|
||||
item_list::iterator iter;
|
||||
for (iter = mItemList.begin(); iter < mItemList.end(); )
|
||||
for (iter = mItemList.begin(); iter != mItemList.end(); )
|
||||
{
|
||||
LLScrollListItem* itemp = *iter;
|
||||
if (itemp->getSelected())
|
||||
|
|
|
|||
|
|
@ -667,7 +667,7 @@ attributedStringInfo getSegments(NSAttributedString *str)
|
|||
};
|
||||
|
||||
int string_length = [aString length];
|
||||
unichar text[string_length];
|
||||
unichar *text = new unichar[string_length];
|
||||
attributedStringInfo segments;
|
||||
// I used 'respondsToSelector:@selector(string)'
|
||||
// to judge aString is an attributed string or not.
|
||||
|
|
@ -695,6 +695,8 @@ attributedStringInfo getSegments(NSAttributedString *str)
|
|||
// we must clear the marked text when aString is null.
|
||||
[self unmarkText];
|
||||
}
|
||||
|
||||
delete [] text;
|
||||
} else {
|
||||
if (mHasMarkedText)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -112,7 +112,6 @@ LLWindow::LLWindow(LLWindowCallbacks* callbacks, bool fullscreen, U32 flags)
|
|||
mFullscreen(fullscreen),
|
||||
mFullscreenWidth(0),
|
||||
mFullscreenHeight(0),
|
||||
mFullscreenBits(0),
|
||||
mFullscreenRefresh(0),
|
||||
mSupportedResolutions(NULL),
|
||||
mNumSupportedResolutions(0),
|
||||
|
|
|
|||
|
|
@ -224,7 +224,6 @@ protected:
|
|||
bool mFullscreen;
|
||||
S32 mFullscreenWidth;
|
||||
S32 mFullscreenHeight;
|
||||
S32 mFullscreenBits;
|
||||
S32 mFullscreenRefresh;
|
||||
LLWindowResolution* mSupportedResolutions;
|
||||
S32 mNumSupportedResolutions;
|
||||
|
|
|
|||
|
|
@ -778,12 +778,10 @@ bool LLWindowSDL::createContext(int x, int y, int width, int height, int bits, b
|
|||
mFullscreen = true;
|
||||
mFullscreenWidth = mSurface->w;
|
||||
mFullscreenHeight = mSurface->h;
|
||||
mFullscreenBits = mSurface->format->BitsPerPixel;
|
||||
mFullscreenRefresh = -1;
|
||||
|
||||
LL_INFOS() << "Running at " << mFullscreenWidth
|
||||
<< "x" << mFullscreenHeight
|
||||
<< "x" << mFullscreenBits
|
||||
<< " @ " << mFullscreenRefresh
|
||||
<< LL_ENDL;
|
||||
}
|
||||
|
|
@ -794,7 +792,6 @@ bool LLWindowSDL::createContext(int x, int y, int width, int height, int bits, b
|
|||
mFullscreen = false;
|
||||
mFullscreenWidth = -1;
|
||||
mFullscreenHeight = -1;
|
||||
mFullscreenBits = -1;
|
||||
mFullscreenRefresh = -1;
|
||||
|
||||
std::string error = llformat("Unable to run fullscreen at %d x %d.\nRunning in window.", width, height);
|
||||
|
|
|
|||
|
|
@ -704,8 +704,7 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks,
|
|||
}
|
||||
|
||||
if (dev_mode.dmPelsWidth == width &&
|
||||
dev_mode.dmPelsHeight == height &&
|
||||
dev_mode.dmBitsPerPel == BITS_PER_PIXEL)
|
||||
dev_mode.dmPelsHeight == height)
|
||||
{
|
||||
success = true;
|
||||
if ((dev_mode.dmDisplayFrequency - current_refresh)
|
||||
|
|
@ -745,7 +744,7 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks,
|
|||
// If we found a good resolution, use it.
|
||||
if (success)
|
||||
{
|
||||
success = setDisplayResolution(width, height, BITS_PER_PIXEL, closest_refresh);
|
||||
success = setDisplayResolution(width, height, closest_refresh);
|
||||
}
|
||||
|
||||
// Keep a copy of the actual current device mode in case we minimize
|
||||
|
|
@ -758,7 +757,6 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks,
|
|||
mFullscreen = true;
|
||||
mFullscreenWidth = dev_mode.dmPelsWidth;
|
||||
mFullscreenHeight = dev_mode.dmPelsHeight;
|
||||
mFullscreenBits = dev_mode.dmBitsPerPel;
|
||||
mFullscreenRefresh = dev_mode.dmDisplayFrequency;
|
||||
|
||||
LL_INFOS("Window") << "Running at " << dev_mode.dmPelsWidth
|
||||
|
|
@ -772,7 +770,6 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks,
|
|||
mFullscreen = false;
|
||||
mFullscreenWidth = -1;
|
||||
mFullscreenHeight = -1;
|
||||
mFullscreenBits = -1;
|
||||
mFullscreenRefresh = -1;
|
||||
|
||||
std::map<std::string,std::string> args;
|
||||
|
|
@ -1195,7 +1192,7 @@ bool LLWindowWin32::switchContext(bool fullscreen, const LLCoordScreen& size, bo
|
|||
// If we found a good resolution, use it.
|
||||
if (success)
|
||||
{
|
||||
success = setDisplayResolution(width, height, BITS_PER_PIXEL, closest_refresh);
|
||||
success = setDisplayResolution(width, height, closest_refresh);
|
||||
}
|
||||
|
||||
// Keep a copy of the actual current device mode in case we minimize
|
||||
|
|
@ -1207,7 +1204,6 @@ bool LLWindowWin32::switchContext(bool fullscreen, const LLCoordScreen& size, bo
|
|||
mFullscreen = true;
|
||||
mFullscreenWidth = dev_mode.dmPelsWidth;
|
||||
mFullscreenHeight = dev_mode.dmPelsHeight;
|
||||
mFullscreenBits = dev_mode.dmBitsPerPel;
|
||||
mFullscreenRefresh = dev_mode.dmDisplayFrequency;
|
||||
|
||||
LL_INFOS("Window") << "Running at " << dev_mode.dmPelsWidth
|
||||
|
|
@ -1233,7 +1229,6 @@ bool LLWindowWin32::switchContext(bool fullscreen, const LLCoordScreen& size, bo
|
|||
mFullscreen = false;
|
||||
mFullscreenWidth = -1;
|
||||
mFullscreenHeight = -1;
|
||||
mFullscreenBits = -1;
|
||||
mFullscreenRefresh = -1;
|
||||
|
||||
LL_INFOS("Window") << "Unable to run fullscreen at " << width << "x" << height << LL_ENDL;
|
||||
|
|
@ -3650,7 +3645,7 @@ F32 LLWindowWin32::getPixelAspectRatio()
|
|||
|
||||
// Change display resolution. Returns true if successful.
|
||||
// protected
|
||||
bool LLWindowWin32::setDisplayResolution(S32 width, S32 height, S32 bits, S32 refresh)
|
||||
bool LLWindowWin32::setDisplayResolution(S32 width, S32 height, S32 refresh)
|
||||
{
|
||||
DEVMODE dev_mode;
|
||||
::ZeroMemory(&dev_mode, sizeof(DEVMODE));
|
||||
|
|
@ -3662,7 +3657,6 @@ bool LLWindowWin32::setDisplayResolution(S32 width, S32 height, S32 bits, S32 re
|
|||
{
|
||||
if (dev_mode.dmPelsWidth == width &&
|
||||
dev_mode.dmPelsHeight == height &&
|
||||
dev_mode.dmBitsPerPel == bits &&
|
||||
dev_mode.dmDisplayFrequency == refresh )
|
||||
{
|
||||
// ...display mode identical, do nothing
|
||||
|
|
@ -3674,9 +3668,8 @@ bool LLWindowWin32::setDisplayResolution(S32 width, S32 height, S32 bits, S32 re
|
|||
dev_mode.dmSize = sizeof(dev_mode);
|
||||
dev_mode.dmPelsWidth = width;
|
||||
dev_mode.dmPelsHeight = height;
|
||||
dev_mode.dmBitsPerPel = bits;
|
||||
dev_mode.dmDisplayFrequency = refresh;
|
||||
dev_mode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY;
|
||||
dev_mode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY;
|
||||
|
||||
// CDS_FULLSCREEN indicates that this is a temporary change to the device mode.
|
||||
LONG cds_result = ChangeDisplaySettings(&dev_mode, CDS_FULLSCREEN);
|
||||
|
|
@ -3686,7 +3679,7 @@ bool LLWindowWin32::setDisplayResolution(S32 width, S32 height, S32 bits, S32 re
|
|||
if (!success)
|
||||
{
|
||||
LL_WARNS("Window") << "setDisplayResolution failed, "
|
||||
<< width << "x" << height << "x" << bits << " @ " << refresh << LL_ENDL;
|
||||
<< width << "x" << height << " @ " << refresh << LL_ENDL;
|
||||
}
|
||||
|
||||
return success;
|
||||
|
|
@ -3697,7 +3690,7 @@ bool LLWindowWin32::setFullscreenResolution()
|
|||
{
|
||||
if (mFullscreen)
|
||||
{
|
||||
return setDisplayResolution( mFullscreenWidth, mFullscreenHeight, mFullscreenBits, mFullscreenRefresh);
|
||||
return setDisplayResolution( mFullscreenWidth, mFullscreenHeight, mFullscreenRefresh);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -153,7 +153,7 @@ protected:
|
|||
virtual LLSD getNativeKeyData();
|
||||
|
||||
// Changes display resolution. Returns true if successful
|
||||
bool setDisplayResolution(S32 width, S32 height, S32 bits, S32 refresh);
|
||||
bool setDisplayResolution(S32 width, S32 height, S32 refresh);
|
||||
|
||||
// Go back to last fullscreen display resolution.
|
||||
bool setFullscreenResolution();
|
||||
|
|
|
|||
|
|
@ -199,6 +199,7 @@ set(viewer_SOURCE_FILES
|
|||
#gltf/accessor.cpp
|
||||
#gltf/primitive.cpp
|
||||
#gltf/animation.cpp
|
||||
#gltf/llgltfloader.cpp
|
||||
# </FS:Ansariel> Group GLTF files into their subfolders
|
||||
groupchatlistener.cpp
|
||||
llaccountingcostmanager.cpp
|
||||
|
|
@ -893,6 +894,7 @@ set(viewer_GLTF_SOURCE_FILES
|
|||
gltf/accessor.cpp
|
||||
gltf/primitive.cpp
|
||||
gltf/animation.cpp
|
||||
gltf/llgltfloader.cpp
|
||||
)
|
||||
|
||||
source_group("Source Files\\GLTF" FILES ${viewer_GLTF_SOURCE_FILES})
|
||||
|
|
@ -1037,6 +1039,7 @@ set(viewer_HEADER_FILES
|
|||
#gltf/buffer_util.h
|
||||
#gltf/primitive.h
|
||||
#gltf/animation.h
|
||||
#gltf/llgltfloader.h
|
||||
# </FS:Ansariel> Group GLTF files into their subfolders
|
||||
llaccountingcost.h
|
||||
llaccountingcostmanager.h
|
||||
|
|
@ -1733,6 +1736,7 @@ set(viewer_GLTF_HEADER_FILES
|
|||
gltf/buffer_util.h
|
||||
gltf/primitive.h
|
||||
gltf/animation.h
|
||||
gltf/llgltfloader.h
|
||||
)
|
||||
|
||||
source_group("Header Files\\GLTF" FILES ${viewer_GLTF_HEADER_FILES})
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
7.1.15
|
||||
7.2.0
|
||||
|
|
|
|||
|
|
@ -1522,7 +1522,7 @@
|
|||
<string>http://search.metaverseink.com/opensim/results.jsp?query=[QUERY]&submit=[CATEGORY]</string>
|
||||
</map>
|
||||
|
||||
<key>ImporterDebug</key>
|
||||
<key>ImporterDebugVerboseLogging</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
<string>Enable debug output to more precisely identify sources of import errors. Warning: the output can slow down import on many machines.</string>
|
||||
|
|
@ -1547,7 +1547,7 @@
|
|||
<key>ImporterModelLimit</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
<string>Limits amount of importer generated models for dae files</string>
|
||||
<string>Limits amount of importer generated (when over 8 faces) models for dae and gltf files</string>
|
||||
<key>Persist</key>
|
||||
<integer>1</integer>
|
||||
<key>Type</key>
|
||||
|
|
@ -1555,6 +1555,17 @@
|
|||
<key>Value</key>
|
||||
<integer>768</integer>
|
||||
</map>
|
||||
<key>ImporterDebugMode</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
<string>At 0 does nothing, at 1 dumps skinning data near orifinal file, at 2 dumps skining data and positions/weights of first 5 models, at 3 dumps skinning data and models as llsd</string>
|
||||
<key>Persist</key>
|
||||
<integer>1</integer>
|
||||
<key>Type</key>
|
||||
<string>U32</string>
|
||||
<key>Value</key>
|
||||
<integer>0</integer>
|
||||
</map>
|
||||
<key>ImporterPreprocessDAE</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
|
|
@ -15267,6 +15278,28 @@ Change of this parameter will affect the layout of buttons in notification toast
|
|||
<key>Value</key>
|
||||
<integer>0</integer>
|
||||
</map>
|
||||
<key>StatsFrametimeSampleSeconds</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
<string>The number of seconds to sample extended frametime data (percentiles, stddev).</string>
|
||||
<key>Persist</key>
|
||||
<integer>1</integer>
|
||||
<key>Type</key>
|
||||
<string>S32</string>
|
||||
<key>Value</key>
|
||||
<integer>5</integer>
|
||||
</map>
|
||||
<key>StatsFrametimeEventThreshold</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
<string>The percentage that the frametime difference must exceed in order to register a frametime event. 0.1 = 10%, 0.25 = 25%, etc.</string>
|
||||
<key>Persist</key>
|
||||
<integer>1</integer>
|
||||
<key>Type</key>
|
||||
<string>F32</string>
|
||||
<key>Value</key>
|
||||
<real>0.1</real>
|
||||
</map>
|
||||
<key>SystemLanguage</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
|
|
@ -23408,6 +23441,28 @@ Change of this parameter will affect the layout of buttons in notification toast
|
|||
<key>Value</key>
|
||||
<integer>1</integer>
|
||||
</map>
|
||||
<key>MediaAutoPlayHuds</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
<string>Automatically play HUD media</string>
|
||||
<key>Persist</key>
|
||||
<integer>1</integer>
|
||||
<key>Type</key>
|
||||
<string>Boolean</string>
|
||||
<key>Value</key>
|
||||
<integer>1</integer>
|
||||
</map>
|
||||
<key>MediaFirstClickInteract</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
<string>This setting controls which media (once loaded) does not require a first click to focus before interaction can begin. This allows clicks to be passed directly to media bypassing the focus click requirement. This setting is a bitfield, precomputed values are as follows: Disabled=0; Worn HUDs only=1; Owned objects=2; Friend objects=4; Group objects=8; Landowner objects=16; Any object=32767; All MOAP=32768. For complete details see lltoolpie.h enum MediaFirstClickTypes.</string>
|
||||
<key>Persist</key>
|
||||
<integer>1</integer>
|
||||
<key>Type</key>
|
||||
<string>S32</string>
|
||||
<key>Value</key>
|
||||
<integer>31</integer>
|
||||
</map>
|
||||
|
||||
<key>FSNetMapPhantomOpacity</key>
|
||||
<map>
|
||||
|
|
|
|||
|
|
@ -1457,28 +1457,6 @@
|
|||
<key>Value</key>
|
||||
<integer>1</integer>
|
||||
</map>
|
||||
<key>FSPrimfeedPhotoResolution</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
<string>Last used resolution for Primfeed photos.</string>
|
||||
<key>Persist</key>
|
||||
<integer>1</integer>
|
||||
<key>Type</key>
|
||||
<string>LLSD</string>
|
||||
<key>Value</key>
|
||||
<array/>
|
||||
</map>
|
||||
<key>FlickrPhotoResolution</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
<string>Last used resolution for Primfeed photos.</string>
|
||||
<key>Persist</key>
|
||||
<integer>1</integer>
|
||||
<key>Type</key>
|
||||
<string>LLSD</string>
|
||||
<key>Value</key>
|
||||
<array/>
|
||||
</map>
|
||||
<key>FlickrPhotoRating</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
|
|
|
|||
|
|
@ -798,7 +798,6 @@ bool DAESaver::saveDAE(std::string filename)
|
|||
for (obj_info_t::iterator obj_iter = mObjects.begin(); obj_iter != mObjects.end(); ++obj_iter)
|
||||
{
|
||||
LLViewerObject* obj = obj_iter->first;
|
||||
S32 total_num_vertices = 0;
|
||||
|
||||
std::string name = "";
|
||||
if (name.empty()) name = llformat("prim%d", prim_nr++);
|
||||
|
|
@ -821,7 +820,6 @@ bool DAESaver::saveDAE(std::string filename)
|
|||
if (skipFace(obj->getTE(face_num))) continue;
|
||||
|
||||
const LLVolumeFace* face = (LLVolumeFace*)&obj->getVolume()->getVolumeFace(face_num);
|
||||
total_num_vertices += face->mNumVertices;
|
||||
|
||||
v4adapt verts(face->mPositions);
|
||||
v4adapt norms(face->mNormals);
|
||||
|
|
|
|||
|
|
@ -381,7 +381,7 @@ bool FSFloaterContacts::isItemsFreeOfFriends(const uuid_vec_t& uuids)
|
|||
}
|
||||
|
||||
// static
|
||||
void FSFloaterContacts::onAvatarPicked(const uuid_vec_t& ids, const std::vector<LLAvatarName> names)
|
||||
void FSFloaterContacts::onAvatarPicked(const uuid_vec_t& ids, const std::vector<LLAvatarName>& names)
|
||||
{
|
||||
if (!names.empty() && !ids.empty())
|
||||
{
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ private:
|
|||
bool isItemsFreeOfFriends(const uuid_vec_t& uuids);
|
||||
|
||||
// misc callbacks
|
||||
static void onAvatarPicked(const uuid_vec_t& ids, const std::vector<LLAvatarName> names);
|
||||
static void onAvatarPicked(const uuid_vec_t& ids, const std::vector<LLAvatarName>& names);
|
||||
void onColumnDisplayModeChanged(const std::string& settings_name = "");
|
||||
bool handleFriendsListDragAndDrop(S32 x, S32 y, MASK mask, bool drop,
|
||||
EDragAndDropType cargo_type,
|
||||
|
|
|
|||
|
|
@ -50,14 +50,12 @@
|
|||
#include "llviewermedia.h"
|
||||
#include "lltabcontainer.h"
|
||||
#include "llviewerparcelmgr.h"
|
||||
#include "llviewerregion.h"
|
||||
#include <boost/regex.hpp>
|
||||
#include "llspinctrl.h"
|
||||
|
||||
#include "llviewernetwork.h"
|
||||
#include "llnotificationsutil.h"
|
||||
#include "fsprimfeedauth.h"
|
||||
#include "llviewernetwork.h"
|
||||
|
||||
static LLPanelInjector<FSPrimfeedPhotoPanel> t_panel_photo("fsprimfeedphotopanel");
|
||||
static LLPanelInjector<FSPrimfeedAccountPanel> t_panel_account("fsprimfeedaccountpanel");
|
||||
|
|
@ -66,22 +64,7 @@ static LLPanelInjector<FSPrimfeedAccountPanel> t_panel_account("fsprimfeedaccoun
|
|||
// FSPrimfeedPhotoPanel/////
|
||||
///////////////////////////
|
||||
|
||||
FSPrimfeedPhotoPanel::FSPrimfeedPhotoPanel() :
|
||||
mResolutionComboBox(nullptr),
|
||||
mRefreshBtn(nullptr),
|
||||
mWorkingLabel(nullptr),
|
||||
mThumbnailPlaceholder(nullptr),
|
||||
mDescriptionTextBox(nullptr),
|
||||
mLocationCheckbox(nullptr),
|
||||
mRatingComboBox(nullptr),
|
||||
mStoresComboBox(nullptr),
|
||||
mPostButton(nullptr),
|
||||
mBtnPreview(nullptr),
|
||||
mBigPreviewFloater(nullptr),
|
||||
mCancelButton(nullptr),
|
||||
mCommercialCheckbox(nullptr),
|
||||
mFilterComboBox(nullptr),
|
||||
mPublicGalleryCheckbox(nullptr)
|
||||
FSPrimfeedPhotoPanel::FSPrimfeedPhotoPanel()
|
||||
{
|
||||
mCommitCallbackRegistrar.add("SocialSharing.SendPhoto", [this](LLUICtrl*, const LLSD&) { onSend(); });
|
||||
mCommitCallbackRegistrar.add("SocialSharing.RefreshPhoto", [this](LLUICtrl*, const LLSD&) { onClickNewSnapshot(); });
|
||||
|
|
@ -98,7 +81,7 @@ FSPrimfeedPhotoPanel::FSPrimfeedPhotoPanel() :
|
|||
{
|
||||
if (data["responseType"].asString() == "primfeed_user_info")
|
||||
{
|
||||
this->loadPrimfeedInfo(data);
|
||||
loadPrimfeedInfo(data);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
@ -112,48 +95,49 @@ FSPrimfeedPhotoPanel::~FSPrimfeedPhotoPanel()
|
|||
mPreviewHandle.get()->die();
|
||||
}
|
||||
|
||||
gSavedSettings.setS32("FSLastSnapshotToPrimfeedResolution", getChild<LLComboBox>("resolution_combobox")->getCurrentIndex());
|
||||
gSavedSettings.setS32("FSLastSnapshotToPrimfeedWidth", getChild<LLSpinCtrl>("custom_snapshot_width")->getValue().asInteger());
|
||||
gSavedSettings.setS32("FSLastSnapshotToPrimfeedHeight", getChild<LLSpinCtrl>("custom_snapshot_height")->getValue().asInteger());
|
||||
gSavedSettings.setS32("FSLastSnapshotToPrimfeedResolution", mResolutionComboBox->getCurrentIndex());
|
||||
gSavedSettings.setS32("FSLastSnapshotToPrimfeedWidth", mCustomSnapshotWidth->getValue().asInteger());
|
||||
gSavedSettings.setS32("FSLastSnapshotToPrimfeedHeight", mCustomSnapshotHeight->getValue().asInteger());
|
||||
}
|
||||
|
||||
bool FSPrimfeedPhotoPanel::postBuild()
|
||||
{
|
||||
setVisibleCallback([this](LLUICtrl*, bool visible) { onVisibilityChange(visible); });
|
||||
|
||||
mResolutionComboBox = getChild<LLUICtrl>("resolution_combobox");
|
||||
mResolutionComboBox = getChild<LLComboBox>("resolution_combobox");
|
||||
mResolutionComboBox->setCommitCallback([this](LLUICtrl*, const LLSD&) { updateResolution(true); });
|
||||
mFilterComboBox = getChild<LLUICtrl>("filters_combobox");
|
||||
mFilterComboBox = getChild<LLComboBox>("filters_combobox");
|
||||
mFilterComboBox->setCommitCallback([this](LLUICtrl*, const LLSD&) { updateResolution(true); });
|
||||
mRefreshBtn = getChild<LLUICtrl>("new_snapshot_btn");
|
||||
mRefreshBtn = getChild<LLButton>("new_snapshot_btn");
|
||||
mBtnPreview = getChild<LLButton>("big_preview_btn");
|
||||
mWorkingLabel = getChild<LLUICtrl>("working_lbl");
|
||||
mThumbnailPlaceholder = getChild<LLUICtrl>("thumbnail_placeholder");
|
||||
mDescriptionTextBox = getChild<LLUICtrl>("photo_description");
|
||||
mLocationCheckbox = getChild<LLUICtrl>("add_location_cb");
|
||||
mCommercialCheckbox = getChild<LLUICtrl>("primfeed_commercial_content");
|
||||
mPublicGalleryCheckbox = getChild<LLUICtrl>("primfeed_add_to_public_gallery");
|
||||
mRatingComboBox = getChild<LLUICtrl>("rating_combobox");
|
||||
mLocationCheckbox = getChild<LLCheckBoxCtrl>("add_location_cb");
|
||||
mCommercialCheckbox = getChild<LLCheckBoxCtrl>("primfeed_commercial_content");
|
||||
mPublicGalleryCheckbox = getChild<LLCheckBoxCtrl>("primfeed_add_to_public_gallery");
|
||||
mRatingComboBox = getChild<LLComboBox>("rating_combobox");
|
||||
mStoresComboBox = getChild<LLComboBox>("stores_combobox");
|
||||
mPostButton = getChild<LLUICtrl>("post_photo_btn");
|
||||
mCancelButton = getChild<LLUICtrl>("cancel_photo_btn");
|
||||
mBigPreviewFloater = dynamic_cast<LLFloaterBigPreview*>(LLFloaterReg::getInstance("big_preview"));
|
||||
mPostButton = getChild<LLButton>("post_photo_btn");
|
||||
mCancelButton = getChild<LLButton>("cancel_photo_btn");
|
||||
mCustomSnapshotWidth = getChild<LLSpinCtrl>("custom_snapshot_width");
|
||||
mCustomSnapshotHeight = getChild<LLSpinCtrl>("custom_snapshot_height");
|
||||
mKeepAspectRatioCbx = getChild<LLCheckBoxCtrl>("keep_aspect_ratio");
|
||||
mBigPreviewFloater = LLFloaterReg::getTypedInstance<LLFloaterBigPreview>("big_preview");
|
||||
|
||||
// Update custom resolution controls with lambdas
|
||||
getChild<LLSpinCtrl>("custom_snapshot_width")->setCommitCallback([this](LLUICtrl*, const LLSD&) { updateResolution(true); });
|
||||
getChild<LLSpinCtrl>("custom_snapshot_height")->setCommitCallback([this](LLUICtrl*, const LLSD&) { updateResolution(true); });
|
||||
getChild<LLCheckBoxCtrl>("keep_aspect_ratio")->setCommitCallback([this](LLUICtrl*, const LLSD&) { updateResolution(true); });
|
||||
mCustomSnapshotWidth->setCommitCallback([this](LLUICtrl*, const LLSD&) { updateResolution(true); });
|
||||
mCustomSnapshotHeight->setCommitCallback([this](LLUICtrl*, const LLSD&) { updateResolution(true); });
|
||||
mKeepAspectRatioCbx->setCommitCallback([this](LLUICtrl*, const LLSD&) { updateResolution(true); });
|
||||
|
||||
getChild<LLComboBox>("resolution_combobox")->setCurrentByIndex(gSavedSettings.getS32("FSLastSnapshotToPrimfeedResolution"));
|
||||
getChild<LLSpinCtrl>("custom_snapshot_width")->setValue(gSavedSettings.getS32("FSLastSnapshotToPrimfeedWidth"));
|
||||
getChild<LLSpinCtrl>("custom_snapshot_height")->setValue(gSavedSettings.getS32("FSLastSnapshotToPrimfeedHeight"));
|
||||
mResolutionComboBox->setCurrentByIndex(gSavedSettings.getS32("FSLastSnapshotToPrimfeedResolution"));
|
||||
mCustomSnapshotWidth->setValue(gSavedSettings.getS32("FSLastSnapshotToPrimfeedWidth"));
|
||||
mCustomSnapshotHeight->setValue(gSavedSettings.getS32("FSLastSnapshotToPrimfeedHeight"));
|
||||
|
||||
// Update filter list
|
||||
std::vector<std::string> filter_list = LLImageFiltersManager::getInstance()->getFiltersList();
|
||||
auto* filterbox = static_cast<LLComboBox*>(mFilterComboBox);
|
||||
for (const std::string& filter : filter_list)
|
||||
for (const std::string& filter : LLImageFiltersManager::getInstance()->getFiltersList())
|
||||
{
|
||||
filterbox->add(filter);
|
||||
mFilterComboBox->add(filter);
|
||||
}
|
||||
|
||||
return LLPanel::postBuild();
|
||||
|
|
@ -168,7 +152,6 @@ void FSFloaterPrimfeed::update()
|
|||
}
|
||||
}
|
||||
|
||||
// virtual
|
||||
S32 FSPrimfeedPhotoPanel::notify(const LLSD& info)
|
||||
{
|
||||
if (info.has("snapshot-updating"))
|
||||
|
|
@ -186,7 +169,7 @@ S32 FSPrimfeedPhotoPanel::notify(const LLSD& info)
|
|||
// The refresh button is initially hidden. We show it after the first update,
|
||||
// i.e. after snapshot is taken
|
||||
|
||||
if (LLUICtrl* refresh_button = getRefreshBtn(); !refresh_button->getVisible())
|
||||
if (LLButton* refresh_button = getRefreshBtn(); !refresh_button->getVisible())
|
||||
{
|
||||
refresh_button->setVisible(true);
|
||||
}
|
||||
|
|
@ -201,7 +184,7 @@ void FSPrimfeedPhotoPanel::draw()
|
|||
auto previewp = static_cast<const LLSnapshotLivePreview*>(mPreviewHandle.get());
|
||||
|
||||
// Enable interaction only if no transaction with the service is on-going (prevent duplicated posts)
|
||||
auto can_post = !(FSPrimfeedConnect::instance().isTransactionOngoing()) && FSPrimfeedAuth::isAuthorized();
|
||||
auto can_post = !FSPrimfeedConnect::instance().isTransactionOngoing() && FSPrimfeedAuth::isAuthorized();
|
||||
|
||||
mCancelButton->setEnabled(can_post);
|
||||
mDescriptionTextBox->setEnabled(can_post);
|
||||
|
|
@ -258,8 +241,7 @@ void FSPrimfeedPhotoPanel::draw()
|
|||
|
||||
LLSnapshotLivePreview* FSPrimfeedPhotoPanel::getPreviewView()
|
||||
{
|
||||
auto previewp = (LLSnapshotLivePreview*)mPreviewHandle.get();
|
||||
return previewp;
|
||||
return (LLSnapshotLivePreview*)mPreviewHandle.get();
|
||||
}
|
||||
|
||||
void FSPrimfeedPhotoPanel::onVisibilityChange(bool visible)
|
||||
|
|
@ -268,8 +250,7 @@ void FSPrimfeedPhotoPanel::onVisibilityChange(bool visible)
|
|||
{
|
||||
if (mPreviewHandle.get())
|
||||
{
|
||||
LLSnapshotLivePreview* preview = getPreviewView();
|
||||
if (preview)
|
||||
if (LLSnapshotLivePreview* preview = getPreviewView())
|
||||
{
|
||||
LL_DEBUGS() << "opened, updating snapshot" << LL_ENDL;
|
||||
preview->updateSnapshot(true);
|
||||
|
|
@ -298,8 +279,7 @@ void FSPrimfeedPhotoPanel::onVisibilityChange(bool visible)
|
|||
|
||||
void FSPrimfeedPhotoPanel::onClickNewSnapshot()
|
||||
{
|
||||
LLSnapshotLivePreview* previewp = getPreviewView();
|
||||
if (previewp)
|
||||
if (LLSnapshotLivePreview* previewp = getPreviewView())
|
||||
{
|
||||
previewp->updateSnapshot(true);
|
||||
}
|
||||
|
|
@ -351,7 +331,7 @@ bool FSPrimfeedPhotoPanel::onPrimfeedConnectStateChange(const LLSD& /*data*/)
|
|||
|
||||
void FSPrimfeedPhotoPanel::sendPhoto()
|
||||
{
|
||||
auto ratingToString = [&](int rating)
|
||||
auto ratingToString = [](S32 rating)
|
||||
{
|
||||
static const std::array<std::string, 4> RATING_NAMES = {
|
||||
"general", // 1
|
||||
|
|
@ -361,16 +341,16 @@ void FSPrimfeedPhotoPanel::sendPhoto()
|
|||
};
|
||||
|
||||
// clamp into [1,4]
|
||||
int idx = llclamp(rating, 1, 4) - 1;
|
||||
S32 idx = llclamp(rating, 1, 4) - 1;
|
||||
return RATING_NAMES[idx];
|
||||
};
|
||||
// Get the description (primfeed has no title/tags etc at this point)
|
||||
std::string description = mDescriptionTextBox->getValue().asString();
|
||||
|
||||
// Get the content rating
|
||||
int content_rating = mRatingComboBox->getValue().asInteger();
|
||||
bool post_to_public_gallery = mPublicGalleryCheckbox->getValue().asBoolean();
|
||||
bool commercial_content = mCommercialCheckbox->getValue().asBoolean();
|
||||
S32 content_rating = mRatingComboBox->getValue().asInteger();
|
||||
bool post_to_public_gallery = mPublicGalleryCheckbox->get();
|
||||
bool commercial_content = mCommercialCheckbox->get();
|
||||
std::string store_id = mStoresComboBox->getValue().asString();
|
||||
// Get the image
|
||||
LLSnapshotLivePreview* previewp = getPreviewView();
|
||||
|
|
@ -383,7 +363,7 @@ void FSPrimfeedPhotoPanel::sendPhoto()
|
|||
params["post_to_public_gallery"] = post_to_public_gallery;
|
||||
// Add the location if required
|
||||
|
||||
if (bool add_location = mLocationCheckbox->getValue().asBoolean(); add_location)
|
||||
if (bool add_location = mLocationCheckbox->get(); add_location)
|
||||
{
|
||||
// Get the SLURL for the location
|
||||
LLSLURL slurl;
|
||||
|
|
@ -445,10 +425,7 @@ void FSPrimfeedPhotoPanel::updateControls()
|
|||
|
||||
void FSPrimfeedPhotoPanel::updateResolution(bool do_update)
|
||||
{
|
||||
auto combobox = static_cast<LLComboBox*>(mResolutionComboBox);
|
||||
auto filterbox = static_cast<LLComboBox*>(mFilterComboBox);
|
||||
|
||||
std::string sdstring = combobox->getSelectedValue();
|
||||
std::string sdstring = mResolutionComboBox->getSelectedValue();
|
||||
LLSD sdres;
|
||||
std::stringstream sstream(sdstring);
|
||||
LLSDSerialize::fromNotation(sdres, sstream, sdstring.size());
|
||||
|
|
@ -457,9 +434,9 @@ void FSPrimfeedPhotoPanel::updateResolution(bool do_update)
|
|||
S32 height = sdres[1];
|
||||
|
||||
// Note : index 0 of the filter drop down is assumed to be "No filter" in whichever locale
|
||||
std::string filter_name = (filterbox->getCurrentIndex() ? filterbox->getSimple() : "");
|
||||
std::string filter_name = (mFilterComboBox->getCurrentIndex() > 0 ? mFilterComboBox->getSimple() : "");
|
||||
|
||||
if (auto previewp = static_cast<LLSnapshotLivePreview*>(mPreviewHandle.get()); previewp && combobox->getCurrentIndex() >= 0)
|
||||
if (auto previewp = static_cast<LLSnapshotLivePreview*>(mPreviewHandle.get()); previewp && mResolutionComboBox->getCurrentIndex() >= 0)
|
||||
{
|
||||
checkAspectRatio(width);
|
||||
|
||||
|
|
@ -477,14 +454,12 @@ void FSPrimfeedPhotoPanel::updateResolution(bool do_update)
|
|||
else if (width == -1 || height == -1)
|
||||
{
|
||||
// take resolution from custom size
|
||||
LLSpinCtrl* width_spinner = getChild<LLSpinCtrl>("custom_snapshot_width");
|
||||
LLSpinCtrl* height_spinner = getChild<LLSpinCtrl>("custom_snapshot_height");
|
||||
S32 custom_width = width_spinner->getValue().asInteger();
|
||||
S32 custom_height = height_spinner->getValue().asInteger();
|
||||
S32 custom_width = mCustomSnapshotWidth->getValue().asInteger();
|
||||
S32 custom_height = mCustomSnapshotHeight->getValue().asInteger();
|
||||
if (checkImageSize(previewp, custom_width, custom_height, true, previewp->getMaxImageSize()))
|
||||
{
|
||||
width_spinner->set((F32)custom_width);
|
||||
height_spinner->set((F32)custom_height);
|
||||
mCustomSnapshotWidth->set((F32)custom_width);
|
||||
mCustomSnapshotHeight->set((F32)custom_height);
|
||||
}
|
||||
LL_DEBUGS() << "Setting preview res from custom: " << custom_width << "x" << custom_height << LL_ENDL;
|
||||
previewp->setSize(custom_width, custom_height);
|
||||
|
|
@ -519,10 +494,10 @@ void FSPrimfeedPhotoPanel::updateResolution(bool do_update)
|
|||
}
|
||||
}
|
||||
|
||||
bool custom_resolution = static_cast<LLComboBox*>(mResolutionComboBox)->getSelectedValue().asString() == "[i-1,i-1]";
|
||||
getChild<LLSpinCtrl>("custom_snapshot_width")->setEnabled(custom_resolution);
|
||||
getChild<LLSpinCtrl>("custom_snapshot_height")->setEnabled(custom_resolution);
|
||||
getChild<LLCheckBoxCtrl>("keep_aspect_ratio")->setEnabled(custom_resolution);
|
||||
bool custom_resolution = mResolutionComboBox->getSelectedValue().asString() == "[i-1,i-1]";
|
||||
mCustomSnapshotWidth->setEnabled(custom_resolution);
|
||||
mCustomSnapshotHeight->setEnabled(custom_resolution);
|
||||
mKeepAspectRatioCbx->setEnabled(custom_resolution);
|
||||
}
|
||||
|
||||
void FSPrimfeedPhotoPanel::checkAspectRatio(S32 index)
|
||||
|
|
@ -537,7 +512,7 @@ void FSPrimfeedPhotoPanel::checkAspectRatio(S32 index)
|
|||
}
|
||||
else if (-1 == index)
|
||||
{
|
||||
keep_aspect = getChild<LLCheckBoxCtrl>("keep_aspect_ratio")->get();
|
||||
keep_aspect = mKeepAspectRatioCbx->get();
|
||||
}
|
||||
else // predefined resolution
|
||||
{
|
||||
|
|
@ -579,7 +554,7 @@ void FSPrimfeedPhotoPanel::loadPrimfeedInfo(const LLSD& data)
|
|||
mStoresComboBox->setCurrentByIndex(0);
|
||||
}
|
||||
|
||||
LLUICtrl* FSPrimfeedPhotoPanel::getRefreshBtn()
|
||||
LLButton* FSPrimfeedPhotoPanel::getRefreshBtn()
|
||||
{
|
||||
return mRefreshBtn;
|
||||
}
|
||||
|
|
@ -588,8 +563,7 @@ void FSPrimfeedPhotoPanel::onOpen(const LLSD& key)
|
|||
{
|
||||
// Reauthorise if necessary.
|
||||
FSPrimfeedAuth::initiateAuthRequest();
|
||||
LLSD dummy;
|
||||
onPrimfeedConnectStateChange(dummy);
|
||||
onPrimfeedConnectStateChange(LLSD());
|
||||
}
|
||||
|
||||
void FSPrimfeedPhotoPanel::uploadCallback(bool success, const LLSD& response)
|
||||
|
|
@ -672,13 +646,7 @@ bool FSPrimfeedPhotoPanel::checkImageSize(LLSnapshotLivePreview* previewp, S32&
|
|||
// FSPrimfeedAccountPanel///
|
||||
///////////////////////////
|
||||
|
||||
FSPrimfeedAccountPanel::FSPrimfeedAccountPanel() :
|
||||
mAccountConnectedAsLabel(nullptr),
|
||||
mAccountNameLink(nullptr),
|
||||
mAccountPlan(nullptr),
|
||||
mPanelButtons(nullptr),
|
||||
mConnectButton(nullptr),
|
||||
mDisconnectButton(nullptr)
|
||||
FSPrimfeedAccountPanel::FSPrimfeedAccountPanel()
|
||||
{
|
||||
mCommitCallbackRegistrar.add("SocialSharing.Connect", [this](LLUICtrl*, const LLSD&) { onConnect(); });
|
||||
mCommitCallbackRegistrar.add("SocialSharing.Disconnect", [this](LLUICtrl*, const LLSD&) { onDisconnect(); });
|
||||
|
|
@ -713,9 +681,8 @@ bool FSPrimfeedAccountPanel::postBuild()
|
|||
mAccountConnectedAsLabel = getChild<LLTextBox>("connected_as_label");
|
||||
mAccountNameLink = getChild<LLTextBox>("primfeed_account_name");
|
||||
mAccountPlan = getChild<LLTextBox>("primfeed_account_plan");
|
||||
mPanelButtons = getChild<LLUICtrl>("panel_buttons");
|
||||
mConnectButton = getChild<LLUICtrl>("connect_btn");
|
||||
mDisconnectButton = getChild<LLUICtrl>("disconnect_btn");
|
||||
mConnectButton = getChild<LLButton>("connect_btn");
|
||||
mDisconnectButton = getChild<LLButton>("disconnect_btn");
|
||||
|
||||
LLSD dummy;
|
||||
onPrimfeedConnectStateChange(dummy);
|
||||
|
|
@ -832,15 +799,13 @@ void FSPrimfeedAccountPanel::showConnectedLayout()
|
|||
void FSPrimfeedAccountPanel::onConnect()
|
||||
{
|
||||
FSPrimfeedAuth::initiateAuthRequest();
|
||||
LLSD dummy;
|
||||
onPrimfeedConnectStateChange(dummy);
|
||||
onPrimfeedConnectStateChange(LLSD());
|
||||
}
|
||||
|
||||
void FSPrimfeedAccountPanel::onDisconnect()
|
||||
{
|
||||
FSPrimfeedAuth::resetAuthStatus();
|
||||
LLSD dummy;
|
||||
onPrimfeedConnectStateChange(dummy);
|
||||
onPrimfeedConnectStateChange(LLSD());
|
||||
}
|
||||
|
||||
FSPrimfeedAccountPanel::~FSPrimfeedAccountPanel()
|
||||
|
|
@ -854,12 +819,7 @@ FSPrimfeedAccountPanel::~FSPrimfeedAccountPanel()
|
|||
////////////////////////
|
||||
|
||||
FSFloaterPrimfeed::FSFloaterPrimfeed(const LLSD& key) :
|
||||
LLFloater(key),
|
||||
mPrimfeedPhotoPanel(nullptr),
|
||||
mPrimfeedAccountPanel(nullptr),
|
||||
mStatusErrorText(nullptr),
|
||||
mStatusLoadingText(nullptr),
|
||||
mStatusLoadingIndicator(nullptr)
|
||||
LLFloater(key)
|
||||
{
|
||||
mCommitCallbackRegistrar.add("SocialSharing.Cancel", [this](LLUICtrl*, const LLSD&) { onCancel(); });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,10 +32,12 @@
|
|||
#include "lltextbox.h"
|
||||
#include "llviewertexture.h"
|
||||
|
||||
class LLButton;
|
||||
class LLIconCtrl;
|
||||
class LLCheckBoxCtrl;
|
||||
class LLComboBox;
|
||||
class LLSnapshotLivePreview;
|
||||
class LLSpinCtrl;
|
||||
class LLFloaterBigPreview;
|
||||
|
||||
/*
|
||||
|
|
@ -51,12 +53,12 @@ public:
|
|||
~FSPrimfeedPhotoPanel();
|
||||
|
||||
bool postBuild() override;
|
||||
S32 notify(const LLSD& info);
|
||||
S32 notify(const LLSD& info) override;
|
||||
void draw() override;
|
||||
void loadPrimfeedInfo(const LLSD& data);
|
||||
|
||||
LLSnapshotLivePreview* getPreviewView();
|
||||
void onVisibilityChange(bool new_visibility);
|
||||
void onVisibilityChange(bool new_visibility) override;
|
||||
void onClickNewSnapshot();
|
||||
void onClickBigPreview();
|
||||
void onSend();
|
||||
|
|
@ -68,7 +70,7 @@ public:
|
|||
void updateControls();
|
||||
void updateResolution(bool do_update);
|
||||
void checkAspectRatio(S32 index);
|
||||
LLUICtrl* getRefreshBtn();
|
||||
LLButton* getRefreshBtn();
|
||||
|
||||
void onOpen(const LLSD& key) override;
|
||||
void primfeedAuthResponse(bool success, const LLSD& response);
|
||||
|
|
@ -82,23 +84,27 @@ private:
|
|||
|
||||
LLHandle<LLView> mPreviewHandle;
|
||||
|
||||
LLUICtrl* mResolutionComboBox;
|
||||
LLUICtrl* mFilterComboBox;
|
||||
LLUICtrl* mRefreshBtn;
|
||||
LLUICtrl* mWorkingLabel;
|
||||
LLUICtrl* mThumbnailPlaceholder;
|
||||
LLUICtrl* mDescriptionTextBox;
|
||||
LLUICtrl* mLocationCheckbox;
|
||||
LLComboBox* mResolutionComboBox{ nullptr };
|
||||
LLComboBox* mFilterComboBox{ nullptr };
|
||||
LLButton* mRefreshBtn{ nullptr };
|
||||
LLUICtrl* mWorkingLabel{ nullptr };
|
||||
LLUICtrl* mThumbnailPlaceholder{ nullptr };
|
||||
LLUICtrl* mDescriptionTextBox{ nullptr };
|
||||
LLCheckBoxCtrl* mLocationCheckbox{ nullptr };
|
||||
|
||||
LLUICtrl* mCommercialCheckbox;
|
||||
LLUICtrl* mPublicGalleryCheckbox;
|
||||
LLUICtrl* mRatingComboBox;
|
||||
LLUICtrl* mPostButton;
|
||||
LLUICtrl* mCancelButton;
|
||||
LLButton* mBtnPreview;
|
||||
LLComboBox* mStoresComboBox;
|
||||
LLCheckBoxCtrl* mCommercialCheckbox{ nullptr };
|
||||
LLCheckBoxCtrl* mPublicGalleryCheckbox{ nullptr };
|
||||
LLComboBox* mRatingComboBox{ nullptr };
|
||||
LLButton* mPostButton{ nullptr };
|
||||
LLButton* mCancelButton{ nullptr };
|
||||
LLButton* mBtnPreview{ nullptr };
|
||||
LLComboBox* mStoresComboBox{ nullptr };
|
||||
|
||||
LLFloaterBigPreview* mBigPreviewFloater;
|
||||
LLSpinCtrl* mCustomSnapshotWidth{ nullptr };
|
||||
LLSpinCtrl* mCustomSnapshotHeight{ nullptr };
|
||||
LLCheckBoxCtrl* mKeepAspectRatioCbx{ nullptr };
|
||||
|
||||
LLFloaterBigPreview* mBigPreviewFloater{ nullptr };
|
||||
};
|
||||
|
||||
class FSPrimfeedAccountPanel : public LLPanel
|
||||
|
|
@ -110,7 +116,7 @@ public:
|
|||
void draw() override;
|
||||
|
||||
private:
|
||||
void onVisibilityChange(bool new_visibility);
|
||||
void onVisibilityChange(bool new_visibility) override;
|
||||
void primfeedAuthResponse(bool success, const LLSD& response);
|
||||
bool onPrimfeedConnectStateChange(const LLSD& data);
|
||||
bool onPrimfeedConnectInfoChange();
|
||||
|
|
@ -122,12 +128,11 @@ private:
|
|||
void showDisconnectedLayout();
|
||||
void showConnectedLayout();
|
||||
|
||||
LLTextBox* mAccountConnectedAsLabel;
|
||||
LLTextBox* mAccountNameLink;
|
||||
LLTextBox* mAccountPlan;
|
||||
LLUICtrl* mPanelButtons;
|
||||
LLUICtrl* mConnectButton;
|
||||
LLUICtrl* mDisconnectButton;
|
||||
LLTextBox* mAccountConnectedAsLabel{ nullptr };
|
||||
LLTextBox* mAccountNameLink{ nullptr };
|
||||
LLTextBox* mAccountPlan{ nullptr };
|
||||
LLButton* mConnectButton{ nullptr };
|
||||
LLButton* mDisconnectButton{ nullptr };
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -147,11 +152,11 @@ public:
|
|||
LLSnapshotLivePreview* getPreviewView();
|
||||
|
||||
private:
|
||||
FSPrimfeedPhotoPanel* mPrimfeedPhotoPanel;
|
||||
FSPrimfeedAccountPanel* mPrimfeedAccountPanel;
|
||||
LLTextBox* mStatusErrorText;
|
||||
LLTextBox* mStatusLoadingText;
|
||||
LLUICtrl* mStatusLoadingIndicator;
|
||||
FSPrimfeedPhotoPanel* mPrimfeedPhotoPanel{ nullptr };
|
||||
FSPrimfeedAccountPanel* mPrimfeedAccountPanel{ nullptr };
|
||||
LLTextBox* mStatusErrorText{ nullptr };
|
||||
LLTextBox* mStatusLoadingText{ nullptr };
|
||||
LLUICtrl* mStatusLoadingIndicator{ nullptr };
|
||||
};
|
||||
|
||||
#endif // LL_FSFLOATERPRIMFEED_H
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ public:
|
|||
|
||||
// Overrides
|
||||
void handleSelect() override;
|
||||
bool updateVisiblity();
|
||||
bool updateVisiblity() override;
|
||||
void render() override;
|
||||
void renderNameXYZ(const LLQuaternion& rot);
|
||||
bool handleMouseDown(S32 x, S32 y, MASK mask) override;
|
||||
|
|
|
|||
|
|
@ -185,35 +185,7 @@ void readSelectedGLTFMaterial(std::function<T(const LLGLTFMaterial*)> func, T& v
|
|||
identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue(&select_func, value, has_tolerance, tolerance);
|
||||
}
|
||||
|
||||
void getSelectedGLTFMaterialMaxRepeats(LLGLTFMaterial::TextureInfo channel, F32& repeats, bool& identical)
|
||||
{
|
||||
// The All channel should read base color values
|
||||
if (channel == LLGLTFMaterial::TextureInfo::GLTF_TEXTURE_INFO_COUNT)
|
||||
channel = LLGLTFMaterial::TextureInfo::GLTF_TEXTURE_INFO_BASE_COLOR;
|
||||
|
||||
struct LLSelectedTEGetGLTFMaterialMaxRepeatsFunctor : public LLSelectedTEGetFunctor<F32>
|
||||
{
|
||||
LLSelectedTEGetGLTFMaterialMaxRepeatsFunctor(LLGLTFMaterial::TextureInfo channel) : mChannel(channel) {}
|
||||
virtual ~LLSelectedTEGetGLTFMaterialMaxRepeatsFunctor() {};
|
||||
F32 get(LLViewerObject* object, S32 face) override
|
||||
{
|
||||
const LLTextureEntry* tep = object->getTE(face);
|
||||
const LLGLTFMaterial* render_material = tep->getGLTFRenderMaterial();
|
||||
if (!render_material)
|
||||
return 0.f;
|
||||
|
||||
U32 s_axis = VX;
|
||||
U32 t_axis = VY;
|
||||
LLPrimitive::getTESTAxes(face, &s_axis, &t_axis);
|
||||
F32 repeats_u = render_material->mTextureTransform[mChannel].mScale[VX] / object->getScale().mV[s_axis];
|
||||
F32 repeats_v = render_material->mTextureTransform[mChannel].mScale[VY] / object->getScale().mV[t_axis];
|
||||
return llmax(repeats_u, repeats_v);
|
||||
}
|
||||
|
||||
LLGLTFMaterial::TextureInfo mChannel;
|
||||
} max_repeats_func(channel);
|
||||
identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue(&max_repeats_func, repeats);
|
||||
}
|
||||
void getSelectedGLTFMaterialMaxRepeats(LLGLTFMaterial::TextureInfo channel, F32& repeats, bool& identical); // Defined in llpanelface.cpp
|
||||
|
||||
//
|
||||
// keep LLRenderMaterialFunctor in sync with llmaterialeditor.cpp - Would be nice if we
|
||||
|
|
|
|||
|
|
@ -159,7 +159,7 @@ bool Buffer::prep(Asset& asset)
|
|||
std::string dir = gDirUtilp->getDirName(asset.mFilename);
|
||||
std::string bin_file = dir + gDirUtilp->getDirDelimiter() + mUri;
|
||||
|
||||
std::ifstream file(bin_file, std::ios::binary);
|
||||
llifstream file(bin_file.c_str(), std::ios::binary);
|
||||
if (!file.is_open())
|
||||
{
|
||||
LL_WARNS("GLTF") << "Failed to open file: " << bin_file << LL_ENDL;
|
||||
|
|
|
|||
|
|
@ -50,6 +50,10 @@ namespace LL
|
|||
"KHR_texture_transform"
|
||||
};
|
||||
|
||||
static std::unordered_set<std::string> ExtensionsIgnored = {
|
||||
"KHR_materials_pbrSpecularGlossiness"
|
||||
};
|
||||
|
||||
Material::AlphaMode gltf_alpha_mode_to_enum(const std::string& alpha_mode)
|
||||
{
|
||||
if (alpha_mode == "OPAQUE")
|
||||
|
|
@ -472,11 +476,14 @@ void Asset::update()
|
|||
|
||||
for (auto& image : mImages)
|
||||
{
|
||||
if (image.mTexture.notNull())
|
||||
{ // HACK - force texture to be loaded full rez
|
||||
// TODO: calculate actual vsize
|
||||
image.mTexture->addTextureStats(2048.f * 2048.f);
|
||||
image.mTexture->setBoostLevel(LLViewerTexture::BOOST_HIGH);
|
||||
if (image.mLoadIntoTexturePipe)
|
||||
{
|
||||
if (image.mTexture.notNull())
|
||||
{ // HACK - force texture to be loaded full rez
|
||||
// TODO: calculate actual vsize
|
||||
image.mTexture->addTextureStats(2048.f * 2048.f);
|
||||
image.mTexture->setBoostLevel(LLViewerTexture::BOOST_HIGH);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -486,18 +493,23 @@ void Asset::update()
|
|||
bool Asset::prep()
|
||||
{
|
||||
LL_PROFILE_ZONE_SCOPED_CATEGORY_GLTF;
|
||||
// check required extensions and fail if not supported
|
||||
bool unsupported = false;
|
||||
// check required extensions
|
||||
for (auto& extension : mExtensionsRequired)
|
||||
{
|
||||
if (ExtensionsSupported.find(extension) == ExtensionsSupported.end())
|
||||
{
|
||||
LL_WARNS() << "Unsupported extension: " << extension << LL_ENDL;
|
||||
unsupported = true;
|
||||
if (ExtensionsIgnored.find(extension) == ExtensionsIgnored.end())
|
||||
{
|
||||
LL_WARNS() << "Unsupported extension: " << extension << LL_ENDL;
|
||||
mUnsupportedExtensions.push_back(extension);
|
||||
}
|
||||
else
|
||||
{
|
||||
mIgnoredExtensions.push_back(extension);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (unsupported)
|
||||
if (mUnsupportedExtensions.size() > 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
@ -513,7 +525,7 @@ bool Asset::prep()
|
|||
|
||||
for (auto& image : mImages)
|
||||
{
|
||||
if (!image.prep(*this))
|
||||
if (!image.prep(*this, mLoadIntoVRAM))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
@ -542,102 +554,110 @@ bool Asset::prep()
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// prepare vertex buffers
|
||||
|
||||
// material count is number of materials + 1 for default material
|
||||
U32 mat_count = (U32) mMaterials.size() + 1;
|
||||
|
||||
if (LLGLSLShader::sCurBoundShaderPtr == nullptr)
|
||||
{ // make sure a shader is bound to satisfy mVertexBuffer->setBuffer
|
||||
gDebugProgram.bind();
|
||||
}
|
||||
|
||||
for (S32 double_sided = 0; double_sided < 2; ++double_sided)
|
||||
if (mLoadIntoVRAM)
|
||||
{
|
||||
RenderData& rd = mRenderData[double_sided];
|
||||
for (U32 i = 0; i < LLGLSLShader::NUM_GLTF_VARIANTS; ++i)
|
||||
{
|
||||
rd.mBatches[i].resize(mat_count);
|
||||
// prepare vertex buffers
|
||||
|
||||
// material count is number of materials + 1 for default material
|
||||
U32 mat_count = (U32) mMaterials.size() + 1;
|
||||
|
||||
if (LLGLSLShader::sCurBoundShaderPtr == nullptr)
|
||||
{ // make sure a shader is bound to satisfy mVertexBuffer->setBuffer
|
||||
gDebugProgram.bind();
|
||||
}
|
||||
|
||||
// for each material
|
||||
for (S32 mat_id = -1; mat_id < (S32)mMaterials.size(); ++mat_id)
|
||||
for (S32 double_sided = 0; double_sided < 2; ++double_sided)
|
||||
{
|
||||
// for each shader variant
|
||||
U32 vertex_count[LLGLSLShader::NUM_GLTF_VARIANTS] = { 0 };
|
||||
U32 index_count[LLGLSLShader::NUM_GLTF_VARIANTS] = { 0 };
|
||||
|
||||
S32 ds_mat = mat_id == -1 ? 0 : mMaterials[mat_id].mDoubleSided;
|
||||
if (ds_mat != double_sided)
|
||||
RenderData& rd = mRenderData[double_sided];
|
||||
for (U32 i = 0; i < LLGLSLShader::NUM_GLTF_VARIANTS; ++i)
|
||||
{
|
||||
continue;
|
||||
rd.mBatches[i].resize(mat_count);
|
||||
}
|
||||
|
||||
for (U32 variant = 0; variant < LLGLSLShader::NUM_GLTF_VARIANTS; ++variant)
|
||||
// for each material
|
||||
for (S32 mat_id = -1; mat_id < (S32)mMaterials.size(); ++mat_id)
|
||||
{
|
||||
U32 attribute_mask = 0;
|
||||
// for each mesh
|
||||
for (auto& mesh : mMeshes)
|
||||
// for each shader variant
|
||||
U32 vertex_count[LLGLSLShader::NUM_GLTF_VARIANTS] = { 0 };
|
||||
U32 index_count[LLGLSLShader::NUM_GLTF_VARIANTS] = { 0 };
|
||||
|
||||
S32 ds_mat = mat_id == -1 ? 0 : mMaterials[mat_id].mDoubleSided;
|
||||
if (ds_mat != double_sided)
|
||||
{
|
||||
// for each primitive
|
||||
for (auto& primitive : mesh.mPrimitives)
|
||||
{
|
||||
if (primitive.mMaterial == mat_id && primitive.mShaderVariant == variant)
|
||||
{
|
||||
// accumulate vertex and index counts
|
||||
primitive.mVertexOffset = vertex_count[variant];
|
||||
primitive.mIndexOffset = index_count[variant];
|
||||
|
||||
vertex_count[variant] += primitive.getVertexCount();
|
||||
index_count[variant] += primitive.getIndexCount();
|
||||
|
||||
// all primitives of a given variant and material should all have the same attribute mask
|
||||
llassert(attribute_mask == 0 || primitive.mAttributeMask == attribute_mask);
|
||||
attribute_mask |= primitive.mAttributeMask;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// allocate vertex buffer and pack it
|
||||
if (vertex_count[variant] > 0)
|
||||
for (U32 variant = 0; variant < LLGLSLShader::NUM_GLTF_VARIANTS; ++variant)
|
||||
{
|
||||
U32 mat_idx = mat_id + 1;
|
||||
LLVertexBuffer* vb = new LLVertexBuffer(attribute_mask);
|
||||
|
||||
rd.mBatches[variant][mat_idx].mVertexBuffer = vb;
|
||||
vb->allocateBuffer(vertex_count[variant],
|
||||
index_count[variant] * 2); // hack double index count... TODO: find a better way to indicate 32-bit indices will be used
|
||||
vb->setBuffer();
|
||||
|
||||
#if SHOW_ASSERT
|
||||
U32 attribute_mask = 0;
|
||||
#endif
|
||||
// for each mesh
|
||||
for (auto& mesh : mMeshes)
|
||||
{
|
||||
// for each primitive
|
||||
for (auto& primitive : mesh.mPrimitives)
|
||||
{
|
||||
if (primitive.mMaterial == mat_id && primitive.mShaderVariant == variant)
|
||||
{
|
||||
primitive.upload(vb);
|
||||
// accumulate vertex and index counts
|
||||
primitive.mVertexOffset = vertex_count[variant];
|
||||
primitive.mIndexOffset = index_count[variant];
|
||||
|
||||
vertex_count[variant] += primitive.getVertexCount();
|
||||
index_count[variant] += primitive.getIndexCount();
|
||||
|
||||
// all primitives of a given variant and material should all have the same attribute mask
|
||||
llassert(attribute_mask == 0 || primitive.mAttributeMask == attribute_mask);
|
||||
#if SHOW_ASSERT
|
||||
attribute_mask |= primitive.mAttributeMask;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vb->unmapBuffer();
|
||||
// allocate vertex buffer and pack it
|
||||
if (vertex_count[variant] > 0)
|
||||
{
|
||||
U32 mat_idx = mat_id + 1;
|
||||
#if 0
|
||||
LLVertexBuffer* vb = new LLVertexBuffer(attribute_mask);
|
||||
|
||||
vb->unbind();
|
||||
rd.mBatches[variant][mat_idx].mVertexBuffer = vb;
|
||||
vb->allocateBuffer(vertex_count[variant],
|
||||
index_count[variant] * 2); // hack double index count... TODO: find a better way to indicate 32-bit indices will be used
|
||||
vb->setBuffer();
|
||||
|
||||
for (auto& mesh : mMeshes)
|
||||
{
|
||||
for (auto& primitive : mesh.mPrimitives)
|
||||
{
|
||||
if (primitive.mMaterial == mat_id && primitive.mShaderVariant == variant)
|
||||
{
|
||||
primitive.upload(vb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vb->unmapBuffer();
|
||||
|
||||
vb->unbind();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sanity check that all primitives have a vertex buffer
|
||||
for (auto& mesh : mMeshes)
|
||||
{
|
||||
for (auto& primitive : mesh.mPrimitives)
|
||||
// sanity check that all primitives have a vertex buffer
|
||||
for (auto& mesh : mMeshes)
|
||||
{
|
||||
llassert(primitive.mVertexBuffer.notNull());
|
||||
for (auto& primitive : mesh.mPrimitives)
|
||||
{
|
||||
//llassert(primitive.mVertexBuffer.notNull());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
// build render batches
|
||||
for (S32 node_id = 0; node_id < mNodes.size(); ++node_id)
|
||||
{
|
||||
|
|
@ -664,6 +684,7 @@ bool Asset::prep()
|
|||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -672,13 +693,14 @@ Asset::Asset(const Value& src)
|
|||
*this = src;
|
||||
}
|
||||
|
||||
bool Asset::load(std::string_view filename)
|
||||
bool Asset::load(std::string_view filename, bool loadIntoVRAM)
|
||||
{
|
||||
LL_PROFILE_ZONE_SCOPED_CATEGORY_GLTF;
|
||||
mLoadIntoVRAM = loadIntoVRAM;
|
||||
mFilename = filename;
|
||||
std::string ext = gDirUtilp->getExtension(mFilename);
|
||||
|
||||
std::ifstream file(filename.data(), std::ios::binary);
|
||||
llifstream file(filename.data(), std::ios::binary);
|
||||
if (file.is_open())
|
||||
{
|
||||
std::string str((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
|
||||
|
|
@ -692,7 +714,7 @@ bool Asset::load(std::string_view filename)
|
|||
}
|
||||
else if (ext == "glb")
|
||||
{
|
||||
return loadBinary(str);
|
||||
return loadBinary(str, mLoadIntoVRAM);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -709,8 +731,9 @@ bool Asset::load(std::string_view filename)
|
|||
return false;
|
||||
}
|
||||
|
||||
bool Asset::loadBinary(const std::string& data)
|
||||
bool Asset::loadBinary(const std::string& data, bool loadIntoVRAM)
|
||||
{
|
||||
mLoadIntoVRAM = loadIntoVRAM;
|
||||
// load from binary gltf
|
||||
const U8* ptr = (const U8*)data.data();
|
||||
const U8* end = ptr + data.size();
|
||||
|
|
@ -935,8 +958,9 @@ void Asset::eraseBufferView(S32 bufferView)
|
|||
|
||||
LLViewerFetchedTexture* fetch_texture(const LLUUID& id);
|
||||
|
||||
bool Image::prep(Asset& asset)
|
||||
bool Image::prep(Asset& asset, bool loadIntoVRAM)
|
||||
{
|
||||
mLoadIntoTexturePipe = loadIntoVRAM;
|
||||
LLUUID id;
|
||||
if (mUri.size() == UUID_STR_SIZE && LLUUID::parseUUID(mUri, &id) && id.notNull())
|
||||
{ // loaded from an asset, fetch the texture from the asset system
|
||||
|
|
@ -951,12 +975,12 @@ bool Image::prep(Asset& asset)
|
|||
{ // embedded in a buffer, load the texture from the buffer
|
||||
BufferView& bufferView = asset.mBufferViews[mBufferView];
|
||||
Buffer& buffer = asset.mBuffers[bufferView.mBuffer];
|
||||
|
||||
U8* data = buffer.mData.data() + bufferView.mByteOffset;
|
||||
|
||||
mTexture = LLViewerTextureManager::getFetchedTextureFromMemory(data, bufferView.mByteLength, mMimeType);
|
||||
|
||||
if (mTexture.isNull())
|
||||
if (mLoadIntoTexturePipe)
|
||||
{
|
||||
U8* data = buffer.mData.data() + bufferView.mByteOffset;
|
||||
mTexture = LLViewerTextureManager::getFetchedTextureFromMemory(data, bufferView.mByteLength, mMimeType);
|
||||
}
|
||||
else if (mTexture.isNull() && mLoadIntoTexturePipe)
|
||||
{
|
||||
LL_WARNS("GLTF") << "Failed to load image from buffer:" << LL_ENDL;
|
||||
LL_WARNS("GLTF") << " image: " << mName << LL_ENDL;
|
||||
|
|
@ -971,12 +995,12 @@ bool Image::prep(Asset& asset)
|
|||
std::string img_file = dir + gDirUtilp->getDirDelimiter() + mUri;
|
||||
|
||||
LLUUID tracking_id = LLLocalBitmapMgr::getInstance()->addUnit(img_file);
|
||||
if (tracking_id.notNull())
|
||||
if (tracking_id.notNull() && mLoadIntoTexturePipe)
|
||||
{
|
||||
LLUUID world_id = LLLocalBitmapMgr::getInstance()->getWorldID(tracking_id);
|
||||
mTexture = LLViewerTextureManager::getFetchedTexture(world_id);
|
||||
}
|
||||
else
|
||||
else if (mLoadIntoTexturePipe)
|
||||
{
|
||||
LL_WARNS("GLTF") << "Failed to load image from file:" << LL_ENDL;
|
||||
LL_WARNS("GLTF") << " image: " << mName << LL_ENDL;
|
||||
|
|
@ -991,7 +1015,7 @@ bool Image::prep(Asset& asset)
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!asset.mFilename.empty())
|
||||
if (!asset.mFilename.empty() && mLoadIntoTexturePipe)
|
||||
{ // local preview, boost image so it doesn't discard and force to save raw image in case we save out or upload
|
||||
mTexture->setBoostLevel(LLViewerTexture::BOOST_PREVIEW);
|
||||
mTexture->forceToSaveRawImage(0, F32_MAX);
|
||||
|
|
|
|||
|
|
@ -286,6 +286,7 @@ namespace LL
|
|||
void serialize(boost::json::object& dst) const;
|
||||
};
|
||||
|
||||
// Image is for images that we want to load for the given asset. This acts as an interface into the viewer's texture pipe.
|
||||
class Image
|
||||
{
|
||||
public:
|
||||
|
|
@ -301,6 +302,8 @@ namespace LL
|
|||
S32 mBits = -1;
|
||||
S32 mPixelType = -1;
|
||||
|
||||
bool mLoadIntoTexturePipe = false;
|
||||
|
||||
LLPointer<LLViewerFetchedTexture> mTexture;
|
||||
|
||||
const Image& operator=(const Value& src);
|
||||
|
|
@ -316,7 +319,7 @@ namespace LL
|
|||
// preserve only uri and name
|
||||
void clearData(Asset& asset);
|
||||
|
||||
bool prep(Asset& asset);
|
||||
bool prep(Asset& asset, bool loadIntoVRAM);
|
||||
};
|
||||
|
||||
// Render Batch -- vertex buffer and list of primitives to render using
|
||||
|
|
@ -391,6 +394,10 @@ namespace LL
|
|||
|
||||
// UBO for storing material data
|
||||
U32 mMaterialsUBO = 0;
|
||||
bool mLoadIntoVRAM = false;
|
||||
|
||||
std::vector<std::string> mUnsupportedExtensions;
|
||||
std::vector<std::string> mIgnoredExtensions;
|
||||
|
||||
// prepare for first time use
|
||||
bool prep();
|
||||
|
|
@ -428,12 +435,12 @@ namespace LL
|
|||
// accepts .gltf and .glb files
|
||||
// Any existing data will be lost
|
||||
// returns result of prep() on success
|
||||
bool load(std::string_view filename);
|
||||
bool load(std::string_view filename, bool loadIntoVRAM);
|
||||
|
||||
// load .glb contents from memory
|
||||
// data - binary contents of .glb file
|
||||
// returns result of prep() on success
|
||||
bool loadBinary(const std::string& data);
|
||||
bool loadBinary(const std::string& data, bool loadIntoVRAM);
|
||||
|
||||
const Asset& operator=(const Value& src);
|
||||
void serialize(boost::json::object& dst) const;
|
||||
|
|
|
|||
|
|
@ -158,6 +158,12 @@ namespace LL
|
|||
dst.load3(src);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline void copyVec3<F32, LLColor4U>(F32* src, LLColor4U& dst)
|
||||
{
|
||||
dst.set((U8)(src[0] * 255.f), (U8)(src[1] * 255.f), (U8)(src[2] * 255.f), 255);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline void copyVec3<U16, LLColor4U>(U16* src, LLColor4U& dst)
|
||||
{
|
||||
|
|
@ -369,6 +375,11 @@ namespace LL
|
|||
template<class T>
|
||||
inline void copy(Asset& asset, Accessor& accessor, LLStrider<T>& dst)
|
||||
{
|
||||
if (accessor.mBufferView == INVALID_INDEX)
|
||||
{
|
||||
LL_WARNS("GLTF") << "Invalid buffer" << LL_ENDL;
|
||||
return;
|
||||
}
|
||||
const BufferView& bufferView = asset.mBufferViews[accessor.mBufferView];
|
||||
const Buffer& buffer = asset.mBuffers[bufferView.mBuffer];
|
||||
const U8* src = buffer.mData.data() + bufferView.mByteOffset + accessor.mByteOffset;
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,216 @@
|
|||
/**
|
||||
* @file LLGLTFLoader.h
|
||||
* @brief LLGLTFLoader class definition
|
||||
*
|
||||
* $LicenseInfo:firstyear=2022&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2022, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#ifndef LL_LLGLTFLoader_H
|
||||
#define LL_LLGLTFLoader_H
|
||||
|
||||
#include "tinygltf/tiny_gltf.h"
|
||||
|
||||
#include "asset.h"
|
||||
|
||||
#include "llglheaders.h"
|
||||
#include "lljointdata.h"
|
||||
#include "llmodelloader.h"
|
||||
|
||||
class LLGLTFLoader : public LLModelLoader
|
||||
{
|
||||
public:
|
||||
typedef std::map<std::string, LLImportMaterial> material_map;
|
||||
typedef std::map<std::string, std::string> joint_viewer_parent_map_t;
|
||||
typedef std::map<std::string, glm::mat4> joint_viewer_rest_map_t;
|
||||
typedef std::map<S32, glm::mat4> joint_node_mat4_map_t;
|
||||
|
||||
struct JointNodeData
|
||||
{
|
||||
JointNodeData()
|
||||
: mJointListIdx(-1)
|
||||
, mNodeIdx(-1)
|
||||
, mParentNodeIdx(-1)
|
||||
, mIsValidViewerJoint(false)
|
||||
, mIsParentValidViewerJoint(false)
|
||||
, mIsOverrideValid(false)
|
||||
{
|
||||
|
||||
}
|
||||
S32 mJointListIdx;
|
||||
S32 mNodeIdx;
|
||||
S32 mParentNodeIdx;
|
||||
glm::mat4 mGltfRestMatrix;
|
||||
glm::mat4 mViewerRestMatrix;
|
||||
glm::mat4 mOverrideRestMatrix;
|
||||
glm::mat4 mGltfMatrix;
|
||||
glm::mat4 mOverrideMatrix;
|
||||
std::string mName;
|
||||
bool mIsValidViewerJoint;
|
||||
bool mIsParentValidViewerJoint;
|
||||
bool mIsOverrideValid;
|
||||
};
|
||||
typedef std::map <S32, JointNodeData> joints_data_map_t;
|
||||
typedef std::map <std::string, S32> joints_name_to_node_map_t;
|
||||
|
||||
class LLGLTFImportMaterial : public LLImportMaterial
|
||||
{
|
||||
public:
|
||||
std::string name;
|
||||
LLGLTFImportMaterial() = default;
|
||||
LLGLTFImportMaterial(const LLImportMaterial& mat, const std::string& n) : LLImportMaterial(mat), name(n) {}
|
||||
};
|
||||
|
||||
LLGLTFLoader(std::string filename,
|
||||
S32 lod,
|
||||
LLModelLoader::load_callback_t load_cb,
|
||||
LLModelLoader::joint_lookup_func_t joint_lookup_func,
|
||||
LLModelLoader::texture_load_func_t texture_load_func,
|
||||
LLModelLoader::state_callback_t state_cb,
|
||||
void * opaque_userdata,
|
||||
JointTransformMap & jointTransformMap,
|
||||
JointNameSet & jointsFromNodes,
|
||||
std::map<std::string, std::string, std::less<>> & jointAliasMap,
|
||||
U32 maxJointsPerMesh,
|
||||
U32 modelLimit,
|
||||
U32 debugMode,
|
||||
std::vector<LLJointData> viewer_skeleton); //,
|
||||
//bool preprocess );
|
||||
virtual ~LLGLTFLoader();
|
||||
|
||||
virtual bool OpenFile(const std::string &filename);
|
||||
|
||||
struct GLTFVertex
|
||||
{
|
||||
glm::vec3 position;
|
||||
glm::vec3 normal;
|
||||
glm::vec2 uv0;
|
||||
glm::u16vec4 joints;
|
||||
glm::vec4 weights;
|
||||
};
|
||||
|
||||
protected:
|
||||
LL::GLTF::Asset mGLTFAsset;
|
||||
tinygltf::Model mGltfModel;
|
||||
bool mGltfLoaded = false;
|
||||
bool mApplyXYRotation = false;
|
||||
|
||||
// GLTF isn't aware of viewer's skeleton and uses it's own,
|
||||
// so need to take viewer's joints and use them to
|
||||
// recalculate iverse bind matrices
|
||||
std::vector<LLJointData> mViewerJointData;
|
||||
|
||||
// vector of vectors because of a posibility of having more than one skin
|
||||
typedef std::vector<LLMeshSkinInfo::matrix_list_t> bind_matrices_t;
|
||||
typedef std::vector<std::vector<std::string> > joint_names_t;
|
||||
bind_matrices_t mInverseBindMatrices;
|
||||
bind_matrices_t mAlternateBindMatrices;
|
||||
joint_names_t mJointNames; // empty string when no legal name for a given idx
|
||||
std::vector<std::vector<S32>> mJointUsage; // detect and warn about unsed joints
|
||||
|
||||
// what group a joint belongs to.
|
||||
// For purpose of stripping unused groups when joints are over limit.
|
||||
struct JointGroups
|
||||
{
|
||||
std::string mGroup;
|
||||
std::string mParentGroup;
|
||||
};
|
||||
typedef std::map<std::string, JointGroups, std::less<> > joint_to_group_map_t;
|
||||
joint_to_group_map_t mJointGroups;
|
||||
|
||||
// per skin joint count, needs to be tracked for the sake of limits check.
|
||||
std::vector<S32> mValidJointsCount;
|
||||
|
||||
// Cached material information
|
||||
typedef std::map<S32, LLGLTFImportMaterial> MaterialCache;
|
||||
MaterialCache mMaterialCache;
|
||||
|
||||
private:
|
||||
bool parseMeshes();
|
||||
void computeCombinedNodeTransform(const LL::GLTF::Asset& asset, S32 node_index, glm::mat4& combined_transform) const;
|
||||
void processNodeHierarchy(S32 node_idx, std::map<std::string, S32>& mesh_name_counts, U32 submodel_limit, const LLVolumeParams& volume_params);
|
||||
bool addJointToModelSkin(LLMeshSkinInfo& skin_info, S32 gltf_skin_idx, size_t gltf_joint_idx);
|
||||
LLGLTFImportMaterial processMaterial(S32 material_index, S32 fallback_index);
|
||||
std::string processTexture(S32 texture_index, const std::string& texture_type, const std::string& material_name);
|
||||
bool validateTextureIndex(S32 texture_index, S32& source_index);
|
||||
std::string generateMaterialName(S32 material_index, S32 fallback_index = -1);
|
||||
bool populateModelFromMesh(LLModel* pModel, const std::string& base_name, const LL::GLTF::Mesh &mesh, const LL::GLTF::Node &node, material_map& mats);
|
||||
void populateJointsFromSkin(S32 skin_idx);
|
||||
void populateJointGroups();
|
||||
void addModelToScene(LLModel* pModel, const std::string& model_name, U32 submodel_limit, const LLMatrix4& transformation, const LLVolumeParams& volume_params, const material_map& mats);
|
||||
void buildJointGroup(LLJointData& viewer_data, const std::string& parent_group);
|
||||
void buildOverrideMatrix(LLJointData& data, joints_data_map_t &gltf_nodes, joints_name_to_node_map_t &names_to_nodes, glm::mat4& parent_rest, glm::mat4& support_rest) const;
|
||||
glm::mat4 buildGltfRestMatrix(S32 joint_node_index, const LL::GLTF::Skin& gltf_skin) const;
|
||||
glm::mat4 buildGltfRestMatrix(S32 joint_node_index, const joints_data_map_t& joint_data) const;
|
||||
glm::mat4 computeGltfToViewerSkeletonTransform(const joints_data_map_t& joints_data_map, S32 gltf_node_index, const std::string& joint_name) const;
|
||||
bool checkForXYrotation(const LL::GLTF::Skin& gltf_skin, S32 joint_idx, S32 bind_indx);
|
||||
void checkForXYrotation(const LL::GLTF::Skin& gltf_skin);
|
||||
void checkGlobalJointUsage();
|
||||
|
||||
std::string extractTextureToTempFile(S32 textureIndex, const std::string& texture_type);
|
||||
|
||||
void notifyUnsupportedExtension(bool unsupported);
|
||||
|
||||
static size_t getSuffixPosition(const std::string& label);
|
||||
static std::string getLodlessLabel(const LL::GLTF::Mesh& mesh);
|
||||
|
||||
// bool mPreprocessGLTF;
|
||||
|
||||
/* Below inherited from dae loader - unknown if/how useful here
|
||||
|
||||
void processElement(gltfElement *element, bool &badElement, GLTF *gltf);
|
||||
void processGltfModel(LLModel *model, GLTF *gltf, gltfElement *pRoot, gltfMesh *mesh, gltfSkin *skin);
|
||||
|
||||
material_map getMaterials(LLModel *model, gltfInstance_geometry *instance_geo, GLTF *gltf);
|
||||
LLImportMaterial profileToMaterial(gltfProfile_COMMON *material, GLTF *gltf);
|
||||
LLColor4 getGltfColor(gltfElement *element);
|
||||
|
||||
gltfElement *getChildFromElement(gltfElement *pElement, std::string const &name);
|
||||
|
||||
bool isNodeAJoint(gltfNode *pNode);
|
||||
void processJointNode(gltfNode *pNode, std::map<std::string, LLMatrix4> &jointTransforms);
|
||||
void extractTranslation(gltfTranslate *pTranslate, LLMatrix4 &transform);
|
||||
void extractTranslationViaElement(gltfElement *pTranslateElement, LLMatrix4 &transform);
|
||||
void extractTranslationViaSID(gltfElement *pElement, LLMatrix4 &transform);
|
||||
void buildJointToNodeMappingFromScene(gltfElement *pRoot);
|
||||
void processJointToNodeMapping(gltfNode *pNode);
|
||||
void processChildJoints(gltfNode *pParentNode);
|
||||
|
||||
bool verifyCount(int expected, int result);
|
||||
|
||||
// Verify that a controller matches vertex counts
|
||||
bool verifyController(gltfController *pController);
|
||||
|
||||
static bool addVolumeFacesFromGltfMesh(LLModel *model, gltfMesh *mesh, LLSD &log_msg);
|
||||
static bool createVolumeFacesFromGltfMesh(LLModel *model, gltfMesh *mesh);
|
||||
|
||||
static LLModel *loadModelFromGltfMesh(gltfMesh *mesh);
|
||||
|
||||
// Loads a mesh breaking it into one or more models as necessary
|
||||
// to get around volume face limitations while retaining >8 materials
|
||||
//
|
||||
bool loadModelsFromGltfMesh(gltfMesh *mesh, std::vector<LLModel *> &models_out, U32 submodel_limit);
|
||||
|
||||
static std::string preprocessGLTF(std::string filename);
|
||||
*/
|
||||
|
||||
};
|
||||
#endif // LL_LLGLTFLLOADER_H
|
||||
|
|
@ -317,7 +317,7 @@ void GLTFSceneManager::load(const std::string& filename)
|
|||
{
|
||||
std::shared_ptr<Asset> asset = std::make_shared<Asset>();
|
||||
|
||||
if (asset->load(filename))
|
||||
if (asset->load(filename, true))
|
||||
{
|
||||
gDebugProgram.bind(); // bind a shader to satisfy LLVertexBuffer assertions
|
||||
asset->updateTransforms();
|
||||
|
|
|
|||
|
|
@ -6682,7 +6682,6 @@ void LLAgent::sendAgentSetAppearance()
|
|||
}
|
||||
|
||||
|
||||
S32 transmitted_params = 0;
|
||||
for (LLViewerVisualParam* param = (LLViewerVisualParam*)gAgentAvatarp->getFirstVisualParam();
|
||||
param;
|
||||
param = (LLViewerVisualParam*)gAgentAvatarp->getNextVisualParam())
|
||||
|
|
@ -6696,7 +6695,6 @@ void LLAgent::sendAgentSetAppearance()
|
|||
const F32 param_value = param->getWeight();
|
||||
const U8 new_weight = F32_to_U8(param_value, param->getMinWeight(), param->getMaxWeight());
|
||||
msg->addU8Fast(_PREHASH_ParamValue, new_weight );
|
||||
transmitted_params++;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -204,7 +204,7 @@ LLAgentCamera::LLAgentCamera() :
|
|||
resetPanDiff();
|
||||
resetOrbitDiff();
|
||||
// <FS:Chanayane> Camera roll (from Alchemy)
|
||||
resetCameraRoll();
|
||||
resetCameraRoll();
|
||||
// </FS:Chanayane>
|
||||
}
|
||||
|
||||
|
|
@ -388,7 +388,7 @@ void LLAgentCamera::resetView(bool reset_camera, bool change_camera, bool moveme
|
|||
|
||||
mCameraFOVZoomFactor = 0.f;
|
||||
// <FS:Chanayane> Camera roll (from Alchemy)
|
||||
resetCameraRoll();
|
||||
resetCameraRoll();
|
||||
// </FS:Chanayane>
|
||||
}
|
||||
resetPanDiff();
|
||||
|
|
@ -974,7 +974,7 @@ void LLAgentCamera::resetCameraOrbit()
|
|||
cameraZoomIn(1.f);
|
||||
resetOrbitDiff();
|
||||
// <FS:Chanayane> Camera roll (from Alchemy)
|
||||
resetCameraRoll();
|
||||
resetCameraRoll();
|
||||
// </FS:Chanayane>
|
||||
}
|
||||
|
||||
|
|
@ -2141,16 +2141,6 @@ LLVector3d LLAgentCamera::calcCameraPositionTargetGlobal(bool *hit_limit)
|
|||
isConstrained = true;
|
||||
}
|
||||
}
|
||||
|
||||
// JC - Could constrain camera based on parcel stuff here.
|
||||
// LLViewerRegion *regionp = LLWorld::getInstance()->getRegionFromPosGlobal(camera_position_global);
|
||||
//
|
||||
// if (regionp && !regionp->mParcelOverlay->isBuildCameraAllowed(regionp->getPosRegionFromGlobal(camera_position_global)))
|
||||
// {
|
||||
// camera_position_global = last_position_global;
|
||||
//
|
||||
// isConstrained = true;
|
||||
// }
|
||||
}
|
||||
|
||||
// [RLVa:KB] - Checked: RLVa-2.0.0
|
||||
|
|
@ -2167,9 +2157,9 @@ LLVector3d LLAgentCamera::calcCameraPositionTargetGlobal(bool *hit_limit)
|
|||
bool fCamAvDistClamped, fCamAvDistLocked = false; float nCamAvDistLimitMin, nCamAvDistLimitMax;
|
||||
if ((fCamAvDistClamped = RlvActions::getCameraAvatarDistanceLimits(nCamAvDistLimitMin, nCamAvDistLimitMax)))
|
||||
fCamAvDistLocked = nCamAvDistLimitMin == nCamAvDistLimitMax;
|
||||
bool fCamOriginDistClamped, fCamOriginDistLocked = false; float nCamOriginDistLimitMin, nCamOriginDistLimitMax;
|
||||
if ((fCamOriginDistClamped = RlvActions::getCameraOriginDistanceLimits(nCamOriginDistLimitMin, nCamOriginDistLimitMax)))
|
||||
fCamOriginDistLocked = nCamOriginDistLimitMin == nCamOriginDistLimitMax;
|
||||
float nCamOriginDistLimitMin;
|
||||
float nCamOriginDistLimitMax;
|
||||
const bool fCamOriginDistClamped = RlvActions::getCameraOriginDistanceLimits(nCamOriginDistLimitMin, nCamOriginDistLimitMax);
|
||||
|
||||
// Check focus distance limits
|
||||
if ( (fCamOriginDistClamped) && (!fCamAvDistLocked) )
|
||||
|
|
@ -2803,7 +2793,7 @@ void LLAgentCamera::switchCameraPreset(ECameraPreset preset)
|
|||
resetPanDiff();
|
||||
resetOrbitDiff();
|
||||
// <FS:Chanayane> Camera roll (from Alchemy)
|
||||
resetCameraRoll();
|
||||
resetCameraRoll();
|
||||
// </FS:Chanayane>
|
||||
|
||||
gSavedSettings.setU32("CameraPresetType", mCameraPreset);
|
||||
|
|
@ -3344,7 +3334,7 @@ void LLAgentCamera::storeCameraPosition()
|
|||
LLVector3d forward = LLVector3d(1.0, 0.0, 0.0) * LLViewerCamera::getInstance()->getQuaternion() + getCameraPositionGlobal();
|
||||
gSavedPerAccountSettings.setVector3d("FSStoredCameraFocus", forward);
|
||||
// <FS:Chanayane> Camera roll (from Alchemy)
|
||||
gSavedPerAccountSettings.setF32("ALStoredCameraRoll", mRollAngle);
|
||||
gSavedPerAccountSettings.setF32("ALStoredCameraRoll", mRollAngle);
|
||||
// </FS:Chanayane>
|
||||
|
||||
LLUUID stored_camera_focus_object_id = LLUUID::null;
|
||||
|
|
@ -3391,7 +3381,7 @@ void LLAgentCamera::loadCameraPosition()
|
|||
unlockView();
|
||||
setCameraPosAndFocusGlobal(stored_camera_pos, stored_camera_focus, stored_camera_focus_object_id);
|
||||
// <FS:Chanayane> Camera roll (from Alchemy)
|
||||
mRollAngle = stored_camera_roll;
|
||||
mRollAngle = stored_camera_roll;
|
||||
// </FS:Chanayane>
|
||||
}
|
||||
// </FS:Ansariel> FIRE-7758: Save/load camera position feature
|
||||
|
|
|
|||
|
|
@ -712,12 +712,15 @@ public:
|
|||
void onFetchCompletion();
|
||||
bool isFetchCompleted();
|
||||
bool isTimedOut();
|
||||
// [SL:KB] - Appearance-Fixes
|
||||
bool pollStopped();
|
||||
// [/SL:KB]
|
||||
|
||||
void checkMissingWearables();
|
||||
bool pollMissingWearables();
|
||||
bool isMissingCompleted();
|
||||
void recoverMissingWearable(LLWearableType::EType type);
|
||||
void clearCOFLinksForMissingWearables();
|
||||
// void clearCOFLinksForMissingWearables();
|
||||
|
||||
void onWearableAssetFetch(LLViewerWearable *wearable);
|
||||
void onAllComplete();
|
||||
|
|
@ -726,7 +729,7 @@ public:
|
|||
found_list_t& getFoundList();
|
||||
void eraseTypeToLink(LLWearableType::EType type);
|
||||
void eraseTypeToRecover(LLWearableType::EType type);
|
||||
void setObjItems(const LLInventoryModel::item_array_t& items);
|
||||
// void setObjItems(const LLInventoryModel::item_array_t& items);
|
||||
void setGestItems(const LLInventoryModel::item_array_t& items);
|
||||
bool isMostRecent();
|
||||
void handleLateArrivals();
|
||||
|
|
@ -736,7 +739,7 @@ public:
|
|||
|
||||
private:
|
||||
found_list_t mFoundList;
|
||||
LLInventoryModel::item_array_t mObjItems;
|
||||
// LLInventoryModel::item_array_t mObjItems;
|
||||
LLInventoryModel::item_array_t mGestItems;
|
||||
typedef std::set<S32> type_set_t;
|
||||
type_set_t mTypesToRecover;
|
||||
|
|
@ -813,10 +816,10 @@ void LLWearableHoldingPattern::eraseTypeToRecover(LLWearableType::EType type)
|
|||
mTypesToRecover.erase(type);
|
||||
}
|
||||
|
||||
void LLWearableHoldingPattern::setObjItems(const LLInventoryModel::item_array_t& items)
|
||||
{
|
||||
mObjItems = items;
|
||||
}
|
||||
//void LLWearableHoldingPattern::setObjItems(const LLInventoryModel::item_array_t& items)
|
||||
//{
|
||||
// mObjItems = items;
|
||||
//}
|
||||
|
||||
void LLWearableHoldingPattern::setGestItems(const LLInventoryModel::item_array_t& items)
|
||||
{
|
||||
|
|
@ -923,55 +926,55 @@ void LLWearableHoldingPattern::onAllComplete()
|
|||
|
||||
if (isAgentAvatarValid())
|
||||
{
|
||||
LL_DEBUGS("Avatar") << self_av_string() << "Updating " << mObjItems.size() << " attachments" << LL_ENDL;
|
||||
LLAgentWearables::llvo_vec_t objects_to_remove;
|
||||
LLAgentWearables::llvo_vec_t objects_to_retain;
|
||||
LLInventoryModel::item_array_t items_to_add;
|
||||
|
||||
LLAgentWearables::findAttachmentsAddRemoveInfo(mObjItems,
|
||||
objects_to_remove,
|
||||
objects_to_retain,
|
||||
items_to_add);
|
||||
|
||||
LL_DEBUGS("Avatar") << self_av_string() << "Removing " << objects_to_remove.size()
|
||||
<< " attachments" << LL_ENDL;
|
||||
|
||||
// Here we remove the attachment pos overrides for *all*
|
||||
// attachments, even those that are not being removed. This is
|
||||
// needed to get joint positions all slammed down to their
|
||||
// pre-attachment states.
|
||||
gAgentAvatarp->clearAttachmentOverrides();
|
||||
|
||||
if (objects_to_remove.size() || items_to_add.size())
|
||||
{
|
||||
LL_DEBUGS("Avatar") << "ATT will remove " << objects_to_remove.size()
|
||||
<< " and add " << items_to_add.size() << " items" << LL_ENDL;
|
||||
}
|
||||
|
||||
// Take off the attachments that will no longer be in the outfit.
|
||||
LLAgentWearables::userRemoveMultipleAttachments(objects_to_remove);
|
||||
// LL_DEBUGS("Avatar") << self_av_string() << "Updating " << mObjItems.size() << " attachments" << LL_ENDL;
|
||||
// LLAgentWearables::llvo_vec_t objects_to_remove;
|
||||
// LLAgentWearables::llvo_vec_t objects_to_retain;
|
||||
// LLInventoryModel::item_array_t items_to_add;
|
||||
//
|
||||
// LLAgentWearables::findAttachmentsAddRemoveInfo(mObjItems,
|
||||
// objects_to_remove,
|
||||
// objects_to_retain,
|
||||
// items_to_add);
|
||||
//
|
||||
// LL_DEBUGS("Avatar") << self_av_string() << "Removing " << objects_to_remove.size()
|
||||
// << " attachments" << LL_ENDL;
|
||||
//
|
||||
// // Here we remove the attachment pos overrides for *all*
|
||||
// // attachments, even those that are not being removed. This is
|
||||
// // needed to get joint positions all slammed down to their
|
||||
// // pre-attachment states.
|
||||
// gAgentAvatarp->clearAttachmentOverrides();
|
||||
//
|
||||
// if (objects_to_remove.size() || items_to_add.size())
|
||||
// {
|
||||
// LL_DEBUGS("Avatar") << "ATT will remove " << objects_to_remove.size()
|
||||
// << " and add " << items_to_add.size() << " items" << LL_ENDL;
|
||||
// }
|
||||
//
|
||||
// // Take off the attachments that will no longer be in the outfit.
|
||||
// LLAgentWearables::userRemoveMultipleAttachments(objects_to_remove);
|
||||
|
||||
// Update wearables.
|
||||
LL_INFOS("Avatar") << self_av_string() << "HP " << index() << " updating agent wearables with "
|
||||
<< mResolved << " wearable items " << LL_ENDL;
|
||||
LLAppearanceMgr::instance().updateAgentWearables(this);
|
||||
|
||||
// Restore attachment pos overrides for the attachments that
|
||||
// are remaining in the outfit.
|
||||
for (LLAgentWearables::llvo_vec_t::iterator it = objects_to_retain.begin();
|
||||
it != objects_to_retain.end();
|
||||
++it)
|
||||
{
|
||||
LLViewerObject *objectp = *it;
|
||||
if (!objectp->isAnimatedObject())
|
||||
{
|
||||
gAgentAvatarp->addAttachmentOverridesForObject(objectp);
|
||||
}
|
||||
}
|
||||
|
||||
// Add new attachments to match those requested.
|
||||
LL_DEBUGS("Avatar") << self_av_string() << "Adding " << items_to_add.size() << " attachments" << LL_ENDL;
|
||||
LLAgentWearables::userAttachMultipleAttachments(items_to_add);
|
||||
// // Restore attachment pos overrides for the attachments that
|
||||
// // are remaining in the outfit.
|
||||
// for (LLAgentWearables::llvo_vec_t::iterator it = objects_to_retain.begin();
|
||||
// it != objects_to_retain.end();
|
||||
// ++it)
|
||||
// {
|
||||
// LLViewerObject *objectp = *it;
|
||||
// if (!objectp->isAnimatedObject())
|
||||
// {
|
||||
// gAgentAvatarp->addAttachmentOverridesForObject(objectp);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // Add new attachments to match those requested.
|
||||
// LL_DEBUGS("Avatar") << self_av_string() << "Adding " << items_to_add.size() << " attachments" << LL_ENDL;
|
||||
// LLAgentWearables::userAttachMultipleAttachments(items_to_add);
|
||||
}
|
||||
|
||||
if (isFetchCompleted() && isMissingCompleted())
|
||||
|
|
@ -1009,6 +1012,12 @@ bool LLWearableHoldingPattern::pollFetchCompletion()
|
|||
{
|
||||
// runway skip here?
|
||||
LL_WARNS() << self_av_string() << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << LL_ENDL;
|
||||
|
||||
// [SL:KB] - Appearance-Fixes
|
||||
// If we were signalled to stop then we shouldn't do anything else except poll for when it's safe to delete ourselves
|
||||
doOnIdleRepeating(boost::bind(&LLWearableHoldingPattern::pollStopped, this));
|
||||
return true;
|
||||
// [/SL:KB]
|
||||
}
|
||||
|
||||
bool completed = isFetchCompleted();
|
||||
|
|
@ -1038,6 +1047,11 @@ void recovered_item_link_cb(const LLUUID& item_id, LLWearableType::EType type, L
|
|||
{
|
||||
LL_WARNS() << "HP " << holder->index() << " skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << LL_ENDL;
|
||||
// runway skip here?
|
||||
|
||||
// [SL:KB] - Appearance-Fixes
|
||||
// If we were signalled to stop then we shouldn't do anything else except poll for when it's safe to delete ourselves
|
||||
return;
|
||||
// [/SL:KB]
|
||||
}
|
||||
|
||||
LL_INFOS("Avatar") << "HP " << holder->index() << " recovered item link for type " << type << LL_ENDL;
|
||||
|
|
@ -1129,19 +1143,32 @@ bool LLWearableHoldingPattern::isMissingCompleted()
|
|||
return mTypesToLink.size()==0 && mTypesToRecover.size()==0;
|
||||
}
|
||||
|
||||
void LLWearableHoldingPattern::clearCOFLinksForMissingWearables()
|
||||
// [SL:KB] - Appearance-Fixes
|
||||
bool LLWearableHoldingPattern::pollStopped()
|
||||
{
|
||||
for (found_list_t::iterator it = getFoundList().begin(); it != getFoundList().end(); ++it)
|
||||
// We have to keep on polling until we're sure that all callbacks have completed or they'll cause a crash
|
||||
if (isFetchCompleted() && isMissingCompleted())
|
||||
{
|
||||
LLFoundData &data = *it;
|
||||
if ((data.mWearableType < LLWearableType::WT_COUNT) && (!data.mWearable))
|
||||
{
|
||||
// Wearable link that was never resolved; remove links to it from COF
|
||||
LL_INFOS("Avatar") << self_av_string() << "HP " << index() << " removing link for unresolved item " << data.mItemID.asString() << LL_ENDL;
|
||||
LLAppearanceMgr::instance().removeCOFItemLinks(data.mItemID);
|
||||
}
|
||||
delete this;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// [/SL:KB]
|
||||
//void LLWearableHoldingPattern::clearCOFLinksForMissingWearables()
|
||||
//{
|
||||
// for (found_list_t::iterator it = getFoundList().begin(); it != getFoundList().end(); ++it)
|
||||
// {
|
||||
// LLFoundData &data = *it;
|
||||
// if ((data.mWearableType < LLWearableType::WT_COUNT) && (!data.mWearable))
|
||||
// {
|
||||
// // Wearable link that was never resolved; remove links to it from COF
|
||||
// LL_INFOS("Avatar") << self_av_string() << "HP " << index() << " removing link for unresolved item " << data.mItemID.asString() << LL_ENDL;
|
||||
// LLAppearanceMgr::instance().removeCOFItemLinks(data.mItemID);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
bool LLWearableHoldingPattern::pollMissingWearables()
|
||||
{
|
||||
|
|
@ -1149,6 +1176,12 @@ bool LLWearableHoldingPattern::pollMissingWearables()
|
|||
{
|
||||
// runway skip here?
|
||||
LL_WARNS() << self_av_string() << "skipping because LLWearableHolding pattern is invalid (superceded by later outfit request)" << LL_ENDL;
|
||||
|
||||
// [SL:KB] - Appearance-Fixes
|
||||
// If we were signalled to stop then we shouldn't do anything else except poll for when it's safe to delete ourselves
|
||||
doOnIdleRepeating(boost::bind(&LLWearableHoldingPattern::pollStopped, this));
|
||||
return true;
|
||||
// [/SL:KB]
|
||||
}
|
||||
|
||||
bool timed_out = isTimedOut();
|
||||
|
|
@ -2843,12 +2876,12 @@ void LLAppearanceMgr::updateAppearanceFromCOF(bool enforce_item_restrictions,
|
|||
remove_non_link_items(wear_items);
|
||||
remove_non_link_items(obj_items);
|
||||
remove_non_link_items(gest_items);
|
||||
// [SL:KB] - Patch: Apperance-Misc | Checked: 2010-11-24 (Catznip-2.4)
|
||||
// [SL:KB] - Appearance-Fixes
|
||||
// Since we're following folder links we might have picked up new duplicates, or exceeded MAX_CLOTHING_LAYERS
|
||||
removeDuplicateItems(wear_items);
|
||||
removeDuplicateItems(obj_items);
|
||||
removeDuplicateItems(gest_items);
|
||||
filterWearableItems(wear_items, LLAgentWearables::MAX_CLOTHING_LAYERS, LLAgentWearables::MAX_CLOTHING_LAYERS);
|
||||
filterWearableItems(wear_items, 0, LLAgentWearables::MAX_CLOTHING_LAYERS);
|
||||
// [/SL:KB]
|
||||
|
||||
dumpItemArray(wear_items,"asset_dump: wear_item");
|
||||
|
|
@ -2861,6 +2894,79 @@ void LLAppearanceMgr::updateAppearanceFromCOF(bool enforce_item_restrictions,
|
|||
<< " descendent_count " << cof->getDescendentCount()
|
||||
<< " viewer desc count " << cof->getViewerDescendentCount() << LL_ENDL;
|
||||
}
|
||||
|
||||
// [SL:KB] - Appearance-Fixes
|
||||
// Update attachments to match those requested.
|
||||
if (isAgentAvatarValid())
|
||||
{
|
||||
// Include attachments which should be in COF but don't have their link created yet
|
||||
std::set<LLUUID> pendingAttachments;
|
||||
LLAttachmentsMgr::instance().getPendingAttachments(pendingAttachments);
|
||||
for (const LLUUID& idAttachItem : pendingAttachments)
|
||||
{
|
||||
if ( !gAgentAvatarp->isWearingAttachment(idAttachItem) || isLinkedInCOF(idAttachItem) )
|
||||
{
|
||||
LLAttachmentsMgr::instance().clearPendingAttachmentLink(idAttachItem);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (LLViewerInventoryItem* pAttachItem = gInventory.getItem(idAttachItem))
|
||||
{
|
||||
obj_items.push_back(pAttachItem);
|
||||
}
|
||||
}
|
||||
|
||||
LL_DEBUGS("Avatar") << self_av_string() << "Updating " << obj_items.size() << " attachments" << LL_ENDL;
|
||||
LLAgentWearables::llvo_vec_t objects_to_remove;
|
||||
LLAgentWearables::llvo_vec_t objects_to_retain;
|
||||
LLInventoryModel::item_array_t items_to_add;
|
||||
|
||||
LLAgentWearables::findAttachmentsAddRemoveInfo(obj_items,
|
||||
objects_to_remove,
|
||||
objects_to_retain,
|
||||
items_to_add);
|
||||
|
||||
LL_DEBUGS("Avatar") << self_av_string() << "Removing " << objects_to_remove.size()
|
||||
<< " attachments" << LL_ENDL;
|
||||
|
||||
// Here we remove the attachment pos overrides for *all*
|
||||
// attachments, even those that are not being removed. This is
|
||||
// needed to get joint positions all slammed down to their
|
||||
// pre-attachment states.
|
||||
gAgentAvatarp->clearAttachmentOverrides();
|
||||
|
||||
if (objects_to_remove.size() || items_to_add.size())
|
||||
{
|
||||
LL_DEBUGS("Avatar") << "ATT will remove " << objects_to_remove.size()
|
||||
<< " and add " << items_to_add.size() << " items" << LL_ENDL;
|
||||
}
|
||||
|
||||
// Take off the attachments that will no longer be in the outfit.
|
||||
// (don't remove attachments until avatar is fully loaded - reduces random attaching/detaching/reattaching at log-on)
|
||||
if (gAgentAvatarp->isFullyLoaded())
|
||||
{
|
||||
LLAgentWearables::userRemoveMultipleAttachments(objects_to_remove);
|
||||
}
|
||||
|
||||
// Restore attachment pos overrides for the attachments that
|
||||
// are remaining in the outfit.
|
||||
for (LLAgentWearables::llvo_vec_t::iterator it = objects_to_retain.begin();
|
||||
it != objects_to_retain.end();
|
||||
++it)
|
||||
{
|
||||
LLViewerObject *objectp = *it;
|
||||
if (!objectp->isAnimatedObject())
|
||||
{
|
||||
gAgentAvatarp->addAttachmentOverridesForObject(objectp);
|
||||
}
|
||||
}
|
||||
|
||||
// Add new attachments to match those requested.
|
||||
LL_DEBUGS("Avatar") << self_av_string() << "Adding " << items_to_add.size() << " attachments" << LL_ENDL;
|
||||
LLAgentWearables::userAttachMultipleAttachments(items_to_add);
|
||||
}
|
||||
// [/SL:KB]
|
||||
|
||||
if(!wear_items.size())
|
||||
{
|
||||
LLNotificationsUtil::add("CouldNotPutOnOutfit");
|
||||
|
|
@ -2875,7 +2981,7 @@ void LLAppearanceMgr::updateAppearanceFromCOF(bool enforce_item_restrictions,
|
|||
LLTimer hp_block_timer;
|
||||
LLWearableHoldingPattern* holder = new LLWearableHoldingPattern;
|
||||
|
||||
holder->setObjItems(obj_items);
|
||||
// holder->setObjItems(obj_items);
|
||||
holder->setGestItems(gest_items);
|
||||
|
||||
// Note: can't do normal iteration, because if all the
|
||||
|
|
@ -4606,6 +4712,9 @@ void LLAppearanceMgr::removeItemsFromAvatar(const uuid_vec_t& ids_to_remove, nul
|
|||
if (!cb)
|
||||
cb = new LLUpdateAppearanceOnDestroy(true, true, post_update_func);
|
||||
removeCOFItemLinks(linked_item_id, cb, immediate_delete);
|
||||
// [SL:KB] - Appearance-Fixes
|
||||
LLAttachmentsMgr::instance().clearPendingAttachmentLink(linked_item_id);
|
||||
// [/SL:KB]
|
||||
addDoomedTempAttachment(linked_item_id);
|
||||
}
|
||||
// [/RLVa:KB]
|
||||
|
|
|
|||
|
|
@ -156,6 +156,9 @@ public:
|
|||
// Attachment link management
|
||||
void unregisterAttachment(const LLUUID& item_id);
|
||||
void registerAttachment(const LLUUID& item_id);
|
||||
// [SL:KB] - Appearance-Fixes
|
||||
bool getAttachmentInvLinkEnable() const { return mAttachmentInvLinkEnabled; }
|
||||
// [/SL:KB]
|
||||
void setAttachmentInvLinkEnable(bool val);
|
||||
|
||||
// Add COF link to individual item.
|
||||
|
|
|
|||
|
|
@ -2666,10 +2666,7 @@ void errorCallback(LLError::ELevel level, const std::string &error_string)
|
|||
// Callback for LLError::LLUserWarningMsg
|
||||
void errorHandler(const std::string& title_string, const std::string& message_string, S32 code)
|
||||
{
|
||||
if (!message_string.empty())
|
||||
{
|
||||
OSMessageBox(message_string, title_string.empty() ? LLTrans::getString("MBFatalError") : title_string, OSMB_OK);
|
||||
}
|
||||
// message is going to hang viewer, create marker first
|
||||
switch (code)
|
||||
{
|
||||
case LLError::LLUserWarningMsg::ERROR_OTHER:
|
||||
|
|
@ -2677,6 +2674,10 @@ void errorHandler(const std::string& title_string, const std::string& message_st
|
|||
break;
|
||||
case LLError::LLUserWarningMsg::ERROR_BAD_ALLOC:
|
||||
LLAppViewer::instance()->createErrorMarker(LAST_EXEC_BAD_ALLOC);
|
||||
// When system run out of memory and errorHandler gets called from a thread,
|
||||
// main thread might keep going while OSMessageBox freezes the caller.
|
||||
// Todo: handle it better, but for now disconnect to avoid making things worse
|
||||
gDisconnected = true;
|
||||
break;
|
||||
case LLError::LLUserWarningMsg::ERROR_MISSING_FILES:
|
||||
LLAppViewer::instance()->createErrorMarker(LAST_EXEC_MISSING_FILES);
|
||||
|
|
@ -2684,6 +2685,10 @@ void errorHandler(const std::string& title_string, const std::string& message_st
|
|||
default:
|
||||
break;
|
||||
}
|
||||
if (!message_string.empty())
|
||||
{
|
||||
OSMessageBox(message_string, title_string.empty() ? LLTrans::getString("MBFatalError") : title_string, OSMB_OK);
|
||||
}
|
||||
}
|
||||
|
||||
void LLAppViewer::initLoggingAndGetLastDuration()
|
||||
|
|
@ -6620,12 +6625,31 @@ void LLAppViewer::forceErrorThreadCrash()
|
|||
thread->start();
|
||||
}
|
||||
|
||||
// <FS:ND> Change from std::string to char const*, saving a lot of object construction/destruction per frame
|
||||
//void LLAppViewer::initMainloopTimeout(const std::string& state, F32 secs)
|
||||
void LLAppViewer::initMainloopTimeout( char const* state, F32 secs)
|
||||
// </FS:ND>
|
||||
void LLAppViewer::forceExceptionThreadCrash()
|
||||
{
|
||||
if(!mMainloopTimeout)
|
||||
class LLCrashTestThread : public LLThread
|
||||
{
|
||||
public:
|
||||
|
||||
LLCrashTestThread() : LLThread("Crash logging test thread")
|
||||
{
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
const std::string exception_text = "This is a deliberate exception in a thread";
|
||||
throw std::runtime_error(exception_text);
|
||||
}
|
||||
};
|
||||
|
||||
LL_WARNS() << "This is a deliberate exception in a thread" << LL_ENDL;
|
||||
LLCrashTestThread* thread = new LLCrashTestThread();
|
||||
thread->start();
|
||||
}
|
||||
|
||||
void LLAppViewer::initMainloopTimeout(std::string_view state, F32 secs)
|
||||
{
|
||||
if (!mMainloopTimeout)
|
||||
{
|
||||
mMainloopTimeout = new LLWatchdogTimeout();
|
||||
resumeMainloopTimeout(state, secs);
|
||||
|
|
@ -6634,23 +6658,20 @@ void LLAppViewer::initMainloopTimeout( char const* state, F32 secs)
|
|||
|
||||
void LLAppViewer::destroyMainloopTimeout()
|
||||
{
|
||||
if(mMainloopTimeout)
|
||||
if (mMainloopTimeout)
|
||||
{
|
||||
delete mMainloopTimeout;
|
||||
mMainloopTimeout = NULL;
|
||||
mMainloopTimeout = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// <FS:ND> Change from std::string to char const*, saving a lot of object construction/destruction per frame
|
||||
//void LLAppViewer::resumeMainloopTimeout(const std::string& state, F32 secs)
|
||||
void LLAppViewer::resumeMainloopTimeout( char const* state, F32 secs)
|
||||
// </FS:ND>
|
||||
void LLAppViewer::resumeMainloopTimeout(std::string_view state, F32 secs)
|
||||
{
|
||||
if(mMainloopTimeout)
|
||||
if (mMainloopTimeout)
|
||||
{
|
||||
if(secs < 0.0f)
|
||||
if (secs < 0.0f)
|
||||
{
|
||||
static LLCachedControl<F32> mainloop_timeout(gSavedSettings, "MainloopTimeoutDefault", 60);
|
||||
static LLCachedControl<F32> mainloop_timeout(gSavedSettings, "MainloopTimeoutDefault", 60.f);
|
||||
secs = mainloop_timeout;
|
||||
}
|
||||
|
||||
|
|
@ -6661,22 +6682,19 @@ void LLAppViewer::resumeMainloopTimeout( char const* state, F32 secs)
|
|||
|
||||
void LLAppViewer::pauseMainloopTimeout()
|
||||
{
|
||||
if(mMainloopTimeout)
|
||||
if (mMainloopTimeout)
|
||||
{
|
||||
mMainloopTimeout->stop();
|
||||
}
|
||||
}
|
||||
|
||||
// <FS:ND> Change from std::string to char const*, saving a lot of object construction/destruction per frame
|
||||
//void LLAppViewer::pingMainloopTimeout(const std::string& state, F32 secs)
|
||||
void LLAppViewer::pingMainloopTimeout( char const* state, F32 secs)
|
||||
// </FS:ND>
|
||||
void LLAppViewer::pingMainloopTimeout(std::string_view state, F32 secs)
|
||||
{
|
||||
LL_PROFILE_ZONE_SCOPED_CATEGORY_APP;
|
||||
|
||||
if(mMainloopTimeout)
|
||||
if (mMainloopTimeout)
|
||||
{
|
||||
if(secs < 0.0f)
|
||||
if (secs < 0.0f)
|
||||
{
|
||||
static LLCachedControl<F32> mainloop_timeout(gSavedSettings, "MainloopTimeoutDefault", 60);
|
||||
secs = mainloop_timeout;
|
||||
|
|
|
|||
|
|
@ -176,6 +176,7 @@ public:
|
|||
virtual void forceErrorCoroprocedureCrash();
|
||||
virtual void forceErrorWorkQueueCrash();
|
||||
virtual void forceErrorThreadCrash();
|
||||
virtual void forceExceptionThreadCrash();
|
||||
|
||||
// The list is found in app_settings/settings_files.xml
|
||||
// but since they are used explicitly in code,
|
||||
|
|
@ -198,25 +199,11 @@ public:
|
|||
// For thread debugging.
|
||||
// llstartup needs to control init.
|
||||
// llworld, send_agent_pause() also controls pause/resume.
|
||||
|
||||
// <FS:ND> Change from std::string to char const*, saving a lot of object construction/destruction per frame
|
||||
|
||||
// void initMainloopTimeout(const std::string& state, F32 secs = -1.0f);
|
||||
void initMainloopTimeout( char const *state, F32 secs = -1.0f);
|
||||
|
||||
// </FS:ND>
|
||||
|
||||
void initMainloopTimeout(std::string_view state, F32 secs = -1.0f);
|
||||
void destroyMainloopTimeout();
|
||||
void pauseMainloopTimeout();
|
||||
|
||||
// <FS:ND> Change from std::string to char const*, saving a lot of object construction/destruction per frame
|
||||
|
||||
// void resumeMainloopTimeout(const std::string& state = "", F32 secs = -1.0f);
|
||||
// void pingMainloopTimeout(const std::string& state, F32 secs = -1.0f);
|
||||
void resumeMainloopTimeout( char const *state = "", F32 secs = -1.0f);
|
||||
void pingMainloopTimeout( char const *state, F32 secs = -1.0f);
|
||||
|
||||
// </FS:ND>
|
||||
void resumeMainloopTimeout(std::string_view state = "", F32 secs = -1.0f);
|
||||
void pingMainloopTimeout(std::string_view state, F32 secs = -1.0f);
|
||||
|
||||
// Handle the 'login completed' event.
|
||||
// *NOTE:Mani Fix this for login abstraction!!
|
||||
|
|
|
|||
|
|
@ -46,10 +46,25 @@ const F32 MAX_ATTACHMENT_REQUEST_LIFETIME = 30.0F;
|
|||
const F32 MIN_RETRY_REQUEST_TIME = 5.0F;
|
||||
const F32 MAX_BAD_COF_TIME = 30.0F;
|
||||
|
||||
// [SL:KB] - Appearance-Fixes
|
||||
class LLRegisterAttachmentCallback : public LLRequestServerAppearanceUpdateOnDestroy
|
||||
{
|
||||
public:
|
||||
void fire(const LLUUID& item_id) override
|
||||
{
|
||||
LLAttachmentsMgr::instance().onRegisterAttachmentComplete(item_id);
|
||||
LLRequestServerAppearanceUpdateOnDestroy::fire(item_id);
|
||||
}
|
||||
};
|
||||
// [/SL:KB]
|
||||
|
||||
LLAttachmentsMgr::LLAttachmentsMgr():
|
||||
mAttachmentRequests("attach",MIN_RETRY_REQUEST_TIME),
|
||||
mDetachRequests("detach",MIN_RETRY_REQUEST_TIME),
|
||||
mQuestionableCOFLinks("badcof",MAX_BAD_COF_TIME)
|
||||
// [SL:KB] - Appearance-Fixes
|
||||
mDetachRequests("detach",MIN_RETRY_REQUEST_TIME)
|
||||
// [/SL:KB]
|
||||
// mDetachRequests("detach",MIN_RETRY_REQUEST_TIME),
|
||||
// mQuestionableCOFLinks("badcof",MAX_BAD_COF_TIME)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -66,6 +81,11 @@ void LLAttachmentsMgr::addAttachmentRequest(const LLUUID& item_id,
|
|||
const bool add, const bool fRlvForce /*=false*/)
|
||||
// [/RLVa:KB]
|
||||
{
|
||||
// [SL:KB] - Appearance-Fixes
|
||||
if (item_id.isNull())
|
||||
return;
|
||||
// [/SL:KB]
|
||||
|
||||
LLViewerInventoryItem *item = gInventory.getItem(item_id);
|
||||
|
||||
if (mAttachmentRequests.wasRequestedRecently(item_id))
|
||||
|
|
@ -142,7 +162,7 @@ void LLAttachmentsMgr::onIdle()
|
|||
|
||||
expireOldDetachRequests();
|
||||
|
||||
checkInvalidCOFLinks();
|
||||
// checkInvalidCOFLinks();
|
||||
|
||||
spamStatusInfo();
|
||||
}
|
||||
|
|
@ -254,6 +274,13 @@ void LLAttachmentsMgr::linkRecentlyArrivedAttachments()
|
|||
{
|
||||
if (mRecentlyArrivedAttachments.size())
|
||||
{
|
||||
// [SL:KB] - Appearance-Fixes
|
||||
if (!LLAppearanceMgr::instance().getAttachmentInvLinkEnable())
|
||||
{
|
||||
return;
|
||||
}
|
||||
// [/SL:KB]
|
||||
|
||||
// One or more attachments have arrived but have not yet been
|
||||
// processed for COF links
|
||||
if (mAttachmentRequests.empty())
|
||||
|
|
@ -300,17 +327,59 @@ void LLAttachmentsMgr::linkRecentlyArrivedAttachments()
|
|||
}
|
||||
if (ids_to_link.size())
|
||||
{
|
||||
LLPointer<LLInventoryCallback> cb = new LLRequestServerAppearanceUpdateOnDestroy();
|
||||
for (uuid_vec_t::const_iterator uuid_it = ids_to_link.begin();
|
||||
uuid_it != ids_to_link.end(); ++uuid_it)
|
||||
// [SL:KB] - Appearance-Fixes
|
||||
LLPointer<LLInventoryCallback> cb = new LLRegisterAttachmentCallback();
|
||||
for (const LLUUID& id_item: ids_to_link)
|
||||
{
|
||||
LLAppearanceMgr::instance().addCOFItemLink(*uuid_it, cb);
|
||||
if (std::find(mPendingAttachLinks.begin(), mPendingAttachLinks.end(), id_item) == mPendingAttachLinks.end())
|
||||
{
|
||||
LLAppearanceMgr::instance().addCOFItemLink(id_item, cb);
|
||||
mPendingAttachLinks.insert(id_item);
|
||||
}
|
||||
}
|
||||
// [/SL:KB]
|
||||
// LLPointer<LLInventoryCallback> cb = new LLRequestServerAppearanceUpdateOnDestroy();
|
||||
// for (uuid_vec_t::const_iterator uuid_it = ids_to_link.begin();
|
||||
// uuid_it != ids_to_link.end(); ++uuid_it)
|
||||
// {
|
||||
// LLAppearanceMgr::instance().addCOFItemLink(*uuid_it, cb);
|
||||
// }
|
||||
}
|
||||
mRecentlyArrivedAttachments.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// [SL:KB] - Appearance-Fixes
|
||||
bool LLAttachmentsMgr::getPendingAttachments(std::set<LLUUID>& ids) const
|
||||
{
|
||||
ids.clear();
|
||||
|
||||
// Returns the combined set of attachments that are pending link creation and those that currently have an ongoing link creation process.
|
||||
set_union(mRecentlyArrivedAttachments.begin(), mRecentlyArrivedAttachments.end(), mPendingAttachLinks.begin(), mPendingAttachLinks.end(), std::inserter(ids, ids.begin()));
|
||||
|
||||
return !ids.empty();
|
||||
}
|
||||
|
||||
void LLAttachmentsMgr::clearPendingAttachmentLink(const LLUUID& idItem)
|
||||
{
|
||||
mPendingAttachLinks.erase(idItem);
|
||||
}
|
||||
|
||||
void LLAttachmentsMgr::onRegisterAttachmentComplete(const LLUUID& id_item_link)
|
||||
{
|
||||
if (const LLUUID& id_item = gInventory.getLinkedItemID(id_item_link); id_item != id_item_link)
|
||||
{
|
||||
clearPendingAttachmentLink(id_item);
|
||||
|
||||
// It may have been detached already in which case we should remove the COF link
|
||||
if (isAgentAvatarValid() && !gAgentAvatarp->isWearingAttachment(id_item))
|
||||
{
|
||||
LLAppearanceMgr::instance().removeCOFItemLinks(id_item);
|
||||
}
|
||||
}
|
||||
}
|
||||
// [/SL:KB]
|
||||
|
||||
LLAttachmentsMgr::LLItemRequestTimes::LLItemRequestTimes(const std::string& op_name, F32 timeout):
|
||||
mOpName(op_name),
|
||||
mTimeout(timeout)
|
||||
|
|
@ -439,6 +508,10 @@ void LLAttachmentsMgr::onDetachRequested(const LLUUID& inv_item_id)
|
|||
|
||||
void LLAttachmentsMgr::onDetachCompleted(const LLUUID& inv_item_id)
|
||||
{
|
||||
// [SL:KB] - Appearance-Fixes
|
||||
clearPendingAttachmentLink(inv_item_id);
|
||||
// [/SL:KB]
|
||||
|
||||
LLTimer timer;
|
||||
LLInventoryItem *item = gInventory.getItem(inv_item_id);
|
||||
if (mDetachRequests.getTime(inv_item_id, timer))
|
||||
|
|
@ -461,9 +534,9 @@ void LLAttachmentsMgr::onDetachCompleted(const LLUUID& inv_item_id)
|
|||
LL_DEBUGS("Avatar") << "ATT detach on shutdown for " << (item ? item->getName() : "UNKNOWN") << " " << inv_item_id << LL_ENDL;
|
||||
}
|
||||
|
||||
LL_DEBUGS("Avatar") << "ATT detached item flagging as questionable for COF link checking "
|
||||
<< (item ? item->getName() : "UNKNOWN") << " id " << inv_item_id << LL_ENDL;
|
||||
mQuestionableCOFLinks.addTime(inv_item_id);
|
||||
// LL_DEBUGS("Avatar") << "ATT detached item flagging as questionable for COF link checking "
|
||||
// << (item ? item->getName() : "UNKNOWN") << " id " << inv_item_id << LL_ENDL;
|
||||
// mQuestionableCOFLinks.addTime(inv_item_id);
|
||||
}
|
||||
|
||||
bool LLAttachmentsMgr::isAttachmentStateComplete() const
|
||||
|
|
@ -472,81 +545,85 @@ bool LLAttachmentsMgr::isAttachmentStateComplete() const
|
|||
&& mAttachmentRequests.empty()
|
||||
&& mDetachRequests.empty()
|
||||
&& mRecentlyArrivedAttachments.empty()
|
||||
&& mQuestionableCOFLinks.empty();
|
||||
}
|
||||
|
||||
// Check for attachments that are (a) linked in COF and (b) not
|
||||
// attached to the avatar. This is a rotten function to have to
|
||||
// include, because it runs the risk of either repeatedly spamming out
|
||||
// COF link removals if they're failing for some reason, or getting
|
||||
// into a tug of war with some other sequence of events that's in the
|
||||
// process of adding the attachment in question. However, it's needed
|
||||
// because we have no definitive source of authority for what things
|
||||
// are actually supposed to be attached. Scripts, run on the server
|
||||
// side, can remove an attachment without our expecting it. If this
|
||||
// happens to an attachment that's just been added, then the COF link
|
||||
// creation may still be in flight, and we will have to delete the
|
||||
// link after it shows up.
|
||||
// [SL:KB] - Appearance-Fixes
|
||||
&& mPendingAttachLinks.empty();
|
||||
}
|
||||
// [/SL:KB]
|
||||
// && mQuestionableCOFLinks.empty();
|
||||
//}
|
||||
//
|
||||
// Note that we only flag items for possible link removal if they have
|
||||
// been previously detached. This means that an attachment failure
|
||||
// will leave the link in the COF, where it will hopefully resolve
|
||||
// correctly on relog.
|
||||
//// Check for attachments that are (a) linked in COF and (b) not
|
||||
//// attached to the avatar. This is a rotten function to have to
|
||||
//// include, because it runs the risk of either repeatedly spamming out
|
||||
//// COF link removals if they're failing for some reason, or getting
|
||||
//// into a tug of war with some other sequence of events that's in the
|
||||
//// process of adding the attachment in question. However, it's needed
|
||||
//// because we have no definitive source of authority for what things
|
||||
//// are actually supposed to be attached. Scripts, run on the server
|
||||
//// side, can remove an attachment without our expecting it. If this
|
||||
//// happens to an attachment that's just been added, then the COF link
|
||||
//// creation may still be in flight, and we will have to delete the
|
||||
//// link after it shows up.
|
||||
////
|
||||
//// Note that we only flag items for possible link removal if they have
|
||||
//// been previously detached. This means that an attachment failure
|
||||
//// will leave the link in the COF, where it will hopefully resolve
|
||||
//// correctly on relog.
|
||||
////
|
||||
//// See related: MAINT-5070, MAINT-4409
|
||||
////
|
||||
//void LLAttachmentsMgr::checkInvalidCOFLinks()
|
||||
//{
|
||||
// if (!gInventory.isInventoryUsable() || mQuestionableCOFLinks.empty())
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
// LLInventoryModel::cat_array_t cat_array;
|
||||
// LLInventoryModel::item_array_t item_array;
|
||||
// gInventory.collectDescendents(LLAppearanceMgr::instance().getCOF(),
|
||||
// cat_array,item_array,LLInventoryModel::EXCLUDE_TRASH);
|
||||
// for (S32 i=0; i<item_array.size(); i++)
|
||||
// {
|
||||
// const LLViewerInventoryItem* inv_item = item_array.at(i).get();
|
||||
// const LLUUID& item_id = inv_item->getLinkedUUID();
|
||||
// if (inv_item->getType() == LLAssetType::AT_OBJECT)
|
||||
// {
|
||||
// LLTimer timer;
|
||||
// bool is_flagged_questionable = mQuestionableCOFLinks.getTime(item_id,timer);
|
||||
// bool is_wearing_attachment = isAgentAvatarValid() && gAgentAvatarp->isWearingAttachment(item_id);
|
||||
// if (is_wearing_attachment && is_flagged_questionable)
|
||||
// {
|
||||
// LL_DEBUGS("Avatar") << "ATT was flagged questionable but is now "
|
||||
// << (is_wearing_attachment ? "attached " : "")
|
||||
// <<"removing flag after "
|
||||
// << timer.getElapsedTimeF32() << " item "
|
||||
// << inv_item->getName() << " id " << item_id << LL_ENDL;
|
||||
// mQuestionableCOFLinks.removeTime(item_id);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// See related: MAINT-5070, MAINT-4409
|
||||
//
|
||||
void LLAttachmentsMgr::checkInvalidCOFLinks()
|
||||
{
|
||||
if (!gInventory.isInventoryUsable() || mQuestionableCOFLinks.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
LLInventoryModel::cat_array_t cat_array;
|
||||
LLInventoryModel::item_array_t item_array;
|
||||
gInventory.collectDescendents(LLAppearanceMgr::instance().getCOF(),
|
||||
cat_array,item_array,LLInventoryModel::EXCLUDE_TRASH);
|
||||
for (S32 i=0; i<item_array.size(); i++)
|
||||
{
|
||||
const LLViewerInventoryItem* inv_item = item_array.at(i).get();
|
||||
const LLUUID& item_id = inv_item->getLinkedUUID();
|
||||
if (inv_item->getType() == LLAssetType::AT_OBJECT)
|
||||
{
|
||||
LLTimer timer;
|
||||
bool is_flagged_questionable = mQuestionableCOFLinks.getTime(item_id,timer);
|
||||
bool is_wearing_attachment = isAgentAvatarValid() && gAgentAvatarp->isWearingAttachment(item_id);
|
||||
if (is_wearing_attachment && is_flagged_questionable)
|
||||
{
|
||||
LL_DEBUGS("Avatar") << "ATT was flagged questionable but is now "
|
||||
<< (is_wearing_attachment ? "attached " : "")
|
||||
<<"removing flag after "
|
||||
<< timer.getElapsedTimeF32() << " item "
|
||||
<< inv_item->getName() << " id " << item_id << LL_ENDL;
|
||||
mQuestionableCOFLinks.removeTime(item_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(LLItemRequestTimes::iterator it = mQuestionableCOFLinks.begin();
|
||||
it != mQuestionableCOFLinks.end(); )
|
||||
{
|
||||
LLItemRequestTimes::iterator curr_it = it;
|
||||
++it;
|
||||
const LLUUID& item_id = curr_it->first;
|
||||
LLViewerInventoryItem *inv_item = gInventory.getItem(item_id);
|
||||
if (curr_it->second.getElapsedTimeF32() > MAX_BAD_COF_TIME)
|
||||
{
|
||||
if (LLAppearanceMgr::instance().isLinkedInCOF(item_id))
|
||||
{
|
||||
LL_DEBUGS("Avatar") << "ATT Linked in COF but not attached or requested, deleting link after "
|
||||
<< curr_it->second.getElapsedTimeF32() << " seconds for "
|
||||
<< (inv_item ? inv_item->getName() : "UNKNOWN") << " id " << item_id << LL_ENDL;
|
||||
LLAppearanceMgr::instance().removeCOFItemLinks(item_id);
|
||||
}
|
||||
mQuestionableCOFLinks.erase(curr_it);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
// for(LLItemRequestTimes::iterator it = mQuestionableCOFLinks.begin();
|
||||
// it != mQuestionableCOFLinks.end(); )
|
||||
// {
|
||||
// LLItemRequestTimes::iterator curr_it = it;
|
||||
// ++it;
|
||||
// const LLUUID& item_id = curr_it->first;
|
||||
// LLViewerInventoryItem *inv_item = gInventory.getItem(item_id);
|
||||
// if (curr_it->second.getElapsedTimeF32() > MAX_BAD_COF_TIME)
|
||||
// {
|
||||
// if (LLAppearanceMgr::instance().isLinkedInCOF(item_id))
|
||||
// {
|
||||
// LL_DEBUGS("Avatar") << "ATT Linked in COF but not attached or requested, deleting link after "
|
||||
// << curr_it->second.getElapsedTimeF32() << " seconds for "
|
||||
// << (inv_item ? inv_item->getName() : "UNKNOWN") << " id " << item_id << LL_ENDL;
|
||||
// LLAppearanceMgr::instance().removeCOFItemLinks(item_id);
|
||||
// }
|
||||
// mQuestionableCOFLinks.erase(curr_it);
|
||||
// continue;
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
void LLAttachmentsMgr::spamStatusInfo()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -90,11 +90,21 @@ public:
|
|||
void onDetachRequested(const LLUUID& inv_item_id);
|
||||
void onDetachCompleted(const LLUUID& inv_item_id);
|
||||
|
||||
// [SL:KB] - Appearance-Fixes
|
||||
void clearPendingAttachmentLink(const LLUUID& idItem);
|
||||
bool getPendingAttachments(std::set<LLUUID>& ids) const;
|
||||
// [/SL:KB]
|
||||
bool isAttachmentStateComplete() const;
|
||||
|
||||
// [SL:KB] - Patch: Appearance-PhantomAttach | Checked: Catznip-5.0
|
||||
void refreshAttachments();
|
||||
|
||||
// [SL:KB] - Appearance-Fixes
|
||||
protected:
|
||||
void onRegisterAttachmentComplete(const LLUUID& id_item_link);
|
||||
friend class LLRegisterAttachmentCallback;
|
||||
// [/SL:KB]
|
||||
|
||||
private:
|
||||
|
||||
class LLItemRequestTimes: public std::map<LLUUID,LLTimer>
|
||||
|
|
@ -117,7 +127,7 @@ private:
|
|||
void linkRecentlyArrivedAttachments();
|
||||
void expireOldAttachmentRequests();
|
||||
void expireOldDetachRequests();
|
||||
void checkInvalidCOFLinks();
|
||||
// void checkInvalidCOFLinks();
|
||||
void spamStatusInfo();
|
||||
|
||||
// Attachments that we are planning to rez but haven't requested from the server yet.
|
||||
|
|
@ -133,8 +143,12 @@ private:
|
|||
std::set<LLUUID> mRecentlyArrivedAttachments;
|
||||
LLTimer mCOFLinkBatchTimer;
|
||||
|
||||
// Attachments that are linked in the COF but may be invalid.
|
||||
LLItemRequestTimes mQuestionableCOFLinks;
|
||||
// [Sl:KB] - Appearance-Fixes
|
||||
// Attachments that have pending COF link creation
|
||||
std::set<LLUUID> mPendingAttachLinks;
|
||||
// [/SL:KB]
|
||||
// // Attachments that are linked in the COF but may be invalid.
|
||||
// LLItemRequestTimes mQuestionableCOFLinks;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ void LLDrawPoolWater::renderPostDeferred(S32 pass)
|
|||
gGL.setColorMask(true, true);
|
||||
|
||||
LLColor3 light_diffuse(0, 0, 0);
|
||||
F32 light_exp = 0.0f;
|
||||
//F32 light_exp = 0.0f; // <FS:Ansariel> Remove unused variable
|
||||
|
||||
LLEnvironment& environment = LLEnvironment::instance();
|
||||
LLSettingsWater::ptr_t pwater = environment.getCurrentWater();
|
||||
|
|
@ -183,7 +183,7 @@ void LLDrawPoolWater::renderPostDeferred(S32 pass)
|
|||
// Apply magic numbers translating light direction into intensities
|
||||
light_dir.normalize();
|
||||
F32 ground_proj_sq = light_dir.mV[0] * light_dir.mV[0] + light_dir.mV[1] * light_dir.mV[1];
|
||||
light_exp = llmax(32.f, 256.f * powf(ground_proj_sq, 16.0f));
|
||||
//light_exp = llmax(32.f, 256.f * powf(ground_proj_sq, 16.0f)); // <FS:Ansariel> Remove unused variable
|
||||
if (0.f < light_diffuse.normalize()) // Normalizing a color? Puzzling...
|
||||
{
|
||||
light_diffuse *= (1.5f + (6.f * ground_proj_sq));
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ LLFilePicker LLFilePicker::sInstance;
|
|||
#define XML_FILTER L"XML files (*.xml)\0*.xml\0"
|
||||
#define SLOBJECT_FILTER L"Objects (*.slobject)\0*.slobject\0"
|
||||
#define RAW_FILTER L"RAW files (*.raw)\0*.raw\0"
|
||||
#define MODEL_FILTER L"Model files (*.dae)\0*.dae\0"
|
||||
#define MODEL_FILTER L"Model files (*.dae, *.gltf, *.glb)\0*.dae;*.gltf;*.glb\0"
|
||||
#define MATERIAL_FILTER L"GLTF Files (*.gltf; *.glb)\0*.gltf;*.glb\0"
|
||||
#define HDRI_FILTER L"HDRI Files (*.exr)\0*.exr\0"
|
||||
#define MATERIAL_TEXTURES_FILTER L"GLTF Import (*.gltf; *.glb; *.tga; *.bmp; *.jpg; *.jpeg; *.png)\0*.gltf;*.glb;*.tga;*.bmp;*.jpg;*.jpeg;*.png\0"
|
||||
|
|
@ -237,6 +237,8 @@ bool LLFilePicker::setupFilter(ELoadFilter filter)
|
|||
break;
|
||||
case FFLOAD_MODEL:
|
||||
mOFN.lpstrFilter = MODEL_FILTER \
|
||||
COLLADA_FILTER \
|
||||
MATERIAL_FILTER \
|
||||
L"\0";
|
||||
break;
|
||||
case FFLOAD_MATERIAL:
|
||||
|
|
@ -734,6 +736,8 @@ std::unique_ptr<std::vector<std::string>> LLFilePicker::navOpenFilterProc(ELoadF
|
|||
allowedv->push_back("exr");
|
||||
break;
|
||||
case FFLOAD_MODEL:
|
||||
allowedv->push_back("gltf");
|
||||
allowedv->push_back("glb");
|
||||
case FFLOAD_COLLADA:
|
||||
allowedv->push_back("dae");
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -217,7 +217,7 @@ void LLFloaterBvhPreview::setAnimCallbacks()
|
|||
getChild<LLUICtrl>("ease_out_time")->setValidateBeforeCommit( boost::bind(&LLFloaterBvhPreview::validateEaseOut, this, _1));
|
||||
}
|
||||
|
||||
std::map <std::string, std::string, std::less<>> LLFloaterBvhPreview::getJointAliases()
|
||||
std::map<std::string, std::string, std::less<>> LLFloaterBvhPreview::getJointAliases()
|
||||
{
|
||||
LLPointer<LLVOAvatar> av = (LLVOAvatar*)mAnimPreview->getDummyAvatar();
|
||||
return av->getJointAliases();
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ public:
|
|||
// </FS:Sei>
|
||||
private:
|
||||
void setAnimCallbacks() ;
|
||||
std::map <std::string, std::string, std::less<>> getJointAliases();
|
||||
std::map<std::string, std::string, std::less<>> getJointAliases();
|
||||
|
||||
// <FS> Reload animation from disk
|
||||
bool loadBVH();
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@
|
|||
#include "llcallbacklist.h"
|
||||
#include "llviewertexteditor.h"
|
||||
#include "llviewernetwork.h"
|
||||
#include "llmaterialeditor.h"
|
||||
|
||||
|
||||
//static
|
||||
|
|
@ -190,7 +191,7 @@ bool LLFloaterModelPreview::postBuild()
|
|||
for (S32 lod = 0; lod <= LLModel::LOD_HIGH; ++lod)
|
||||
{
|
||||
LLComboBox* lod_source_combo = getChild<LLComboBox>("lod_source_" + lod_name[lod]);
|
||||
lod_source_combo->setCommitCallback(boost::bind(&LLFloaterModelPreview::onLoDSourceCommit, this, lod));
|
||||
lod_source_combo->setCommitCallback(boost::bind(&LLFloaterModelPreview::onLoDSourceCommit, this, lod, true));
|
||||
lod_source_combo->setCurrentByIndex(mLODMode[lod]);
|
||||
|
||||
getChild<LLButton>("lod_browse_" + lod_name[lod])->setCommitCallback(boost::bind(&LLFloaterModelPreview::onBrowseLOD, this, lod));
|
||||
|
|
@ -708,11 +709,9 @@ void LLFloaterModelPreview::onJointListSelection()
|
|||
LLPanel *panel = mTabContainer->getPanelByName("rigging_panel");
|
||||
LLScrollListCtrl *joints_list = panel->getChild<LLScrollListCtrl>("joints_list");
|
||||
LLScrollListCtrl *joints_pos = panel->getChild<LLScrollListCtrl>("pos_overrides_list");
|
||||
LLScrollListCtrl *joints_scale = panel->getChild<LLScrollListCtrl>("scale_overrides_list");
|
||||
LLTextBox *joint_pos_descr = panel->getChild<LLTextBox>("pos_overrides_descr");
|
||||
|
||||
joints_pos->deleteAllItems();
|
||||
joints_scale->deleteAllItems();
|
||||
|
||||
LLScrollListItem *selected = joints_list->getFirstSelected();
|
||||
if (selected)
|
||||
|
|
@ -859,7 +858,7 @@ void LLFloaterModelPreview::onLODParamCommit(S32 lod, bool enforce_tri_limit)
|
|||
LLComboBox* lod_source_combo = getChild<LLComboBox>("lod_source_" + lod_name[i]);
|
||||
if (lod_source_combo->getCurrentIndex() == LLModelPreview::USE_LOD_ABOVE)
|
||||
{
|
||||
onLoDSourceCommit(i);
|
||||
onLoDSourceCommit(i, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -1524,26 +1523,26 @@ void LLFloaterModelPreview::addStringToLog(const std::string& message, const LLS
|
|||
{
|
||||
std::string str;
|
||||
switch (lod)
|
||||
{
|
||||
{
|
||||
case LLModel::LOD_IMPOSTOR: str = "LOD0 "; break;
|
||||
case LLModel::LOD_LOW: str = "LOD1 "; break;
|
||||
case LLModel::LOD_MEDIUM: str = "LOD2 "; break;
|
||||
case LLModel::LOD_PHYSICS: str = "PHYS "; break;
|
||||
case LLModel::LOD_HIGH: str = "LOD3 "; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
LLStringUtil::format_map_t args_msg;
|
||||
LLSD::map_const_iterator iter = args.beginMap();
|
||||
LLSD::map_const_iterator end = args.endMap();
|
||||
for (; iter != end; ++iter)
|
||||
{
|
||||
{
|
||||
args_msg[iter->first] = iter->second.asString();
|
||||
}
|
||||
str += sInstance->getString(message, args_msg);
|
||||
sInstance->addStringToLogTab(str, flash);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
void LLFloaterModelPreview::addStringToLog(const std::string& str, bool flash)
|
||||
|
|
@ -1944,7 +1943,7 @@ void LLFloaterModelPreview::toggleCalculateButton(bool visible)
|
|||
}
|
||||
}
|
||||
|
||||
void LLFloaterModelPreview::onLoDSourceCommit(S32 lod)
|
||||
void LLFloaterModelPreview::onLoDSourceCommit(S32 lod, bool refresh_ui)
|
||||
{
|
||||
mModelPreview->updateLodControls(lod);
|
||||
|
||||
|
|
@ -1966,9 +1965,17 @@ void LLFloaterModelPreview::onLoDSourceCommit(S32 lod)
|
|||
|| index == LLModelPreview::GENERATE // <FS:Beq/> Improved LOD generation
|
||||
|| index == LLModelPreview::MESH_OPTIMIZER_SLOPPY
|
||||
|| index == LLModelPreview::MESH_OPTIMIZER_PRECISE)
|
||||
{ //rebuild LoD to update triangle counts
|
||||
{
|
||||
// rebuild LoD to update triangle counts
|
||||
onLODParamCommit(lod, true);
|
||||
}
|
||||
else if (refresh_ui && index == LLModelPreview::USE_LOD_ABOVE)
|
||||
{
|
||||
// Update mUploadData for updateStatusMessages
|
||||
mModelPreview->rebuildUploadData();
|
||||
// Update UI with new triangle values
|
||||
mModelPreview->updateStatusMessages();
|
||||
}
|
||||
}
|
||||
|
||||
void LLFloaterModelPreview::resetDisplayOptions()
|
||||
|
|
|
|||
|
|
@ -210,7 +210,7 @@ private:
|
|||
void onClickCalculateBtn();
|
||||
void onJointListSelection();
|
||||
|
||||
void onLoDSourceCommit(S32 lod);
|
||||
void onLoDSourceCommit(S32 lod, bool refresh_ui);
|
||||
|
||||
void modelUpdated(bool calculate_visible);
|
||||
|
||||
|
|
|
|||
|
|
@ -142,15 +142,13 @@
|
|||
#include "llavatarname.h" // <FS:CR> Deeper name cache stuffs
|
||||
#include "llclipboard.h" // <FS:Zi> Support preferences search SLURLs
|
||||
#include "lldiriterator.h" // <Kadah> for populating the fonts combo
|
||||
#include "lleventtimer.h"
|
||||
#include "llline.h"
|
||||
#include "lllocationhistory.h"
|
||||
#include "llpanelblockedlist.h"
|
||||
#include "llpanelmaininventory.h"
|
||||
#include "llscrolllistctrl.h"
|
||||
#include "llsdserialize.h" // KB: SkinsSelector
|
||||
#include "llspellcheck.h"
|
||||
#include "lltoolbarview.h"
|
||||
#include "lltoolpie.h"
|
||||
#include "llviewermenufile.h" // <FS:LO> FIRE-23606 Reveal path to external script editor in prefernces
|
||||
#include "llviewernetwork.h" // <FS:AW opensim search support>
|
||||
#include "llviewershadermgr.h"
|
||||
|
|
@ -6291,6 +6289,13 @@ FSPanelPreferenceSounds::FSPanelPreferenceSounds() :
|
|||
LLPanelPreference(),
|
||||
mOutputDevicePanel(nullptr),
|
||||
mOutputDeviceComboBox(nullptr),
|
||||
mMoapInteractionAll(nullptr),
|
||||
mMoapInteractionAny(nullptr),
|
||||
mMoapInteractionHud(nullptr),
|
||||
mMoapInteractionOwnObjects(nullptr),
|
||||
mMoapInteractionGroupObjects(nullptr),
|
||||
mMoapInteractionFriendObjects(nullptr),
|
||||
mMoapInteractionLandownerObjects(nullptr),
|
||||
mOutputDeviceListChangedConnection()
|
||||
{ }
|
||||
|
||||
|
|
@ -6307,6 +6312,14 @@ bool FSPanelPreferenceSounds::postBuild()
|
|||
mOutputDevicePanel = findChild<LLPanel>("output_device_settings_panel");
|
||||
mOutputDeviceComboBox = findChild<LLComboBox>("sound_output_device");
|
||||
|
||||
mMoapInteractionAll = getChild<LLCheckBoxCtrl>("media_first_click_all");
|
||||
mMoapInteractionAny = getChild<LLCheckBoxCtrl>("media_first_click_any");
|
||||
mMoapInteractionHud = getChild<LLCheckBoxCtrl>("media_first_click_hud");
|
||||
mMoapInteractionOwnObjects = getChild<LLCheckBoxCtrl>("media_first_click_own");
|
||||
mMoapInteractionGroupObjects = getChild<LLCheckBoxCtrl>("media_first_click_group");
|
||||
mMoapInteractionFriendObjects = getChild<LLCheckBoxCtrl>("media_first_click_friend");
|
||||
mMoapInteractionLandownerObjects = getChild<LLCheckBoxCtrl>("media_first_click_land");
|
||||
|
||||
#if LL_FMODSTUDIO
|
||||
if (gAudiop && mOutputDevicePanel && mOutputDeviceComboBox)
|
||||
{
|
||||
|
|
@ -6324,6 +6337,17 @@ bool FSPanelPreferenceSounds::postBuild()
|
|||
}
|
||||
#endif
|
||||
|
||||
mMoapInteractionAll->setCommitCallback(boost::bind(&FSPanelPreferenceSounds::updateMoapInteractionSetting, this));
|
||||
mMoapInteractionAny->setCommitCallback(boost::bind(&FSPanelPreferenceSounds::updateMoapInteractionSetting, this));
|
||||
mMoapInteractionHud->setCommitCallback(boost::bind(&FSPanelPreferenceSounds::updateMoapInteractionSetting, this));
|
||||
mMoapInteractionOwnObjects->setCommitCallback(boost::bind(&FSPanelPreferenceSounds::updateMoapInteractionSetting, this));
|
||||
mMoapInteractionGroupObjects->setCommitCallback(boost::bind(&FSPanelPreferenceSounds::updateMoapInteractionSetting, this));
|
||||
mMoapInteractionFriendObjects->setCommitCallback(boost::bind(&FSPanelPreferenceSounds::updateMoapInteractionSetting, this));
|
||||
mMoapInteractionLandownerObjects->setCommitCallback(boost::bind(&FSPanelPreferenceSounds::updateMoapInteractionSetting, this));
|
||||
|
||||
gSavedSettings.getControl("MediaFirstClickInteract")->getSignal()->connect(boost::bind(&FSPanelPreferenceSounds::onMoapInteractionChanged, this));
|
||||
onMoapInteractionChanged();
|
||||
|
||||
return LLPanelPreference::postBuild();
|
||||
}
|
||||
|
||||
|
|
@ -6380,4 +6404,48 @@ void FSPanelPreferenceSounds::onOutputDeviceListChanged(LLAudioEngine::output_de
|
|||
|
||||
mOutputDeviceComboBox->setSelectedByValue(selected_device, true);
|
||||
}
|
||||
|
||||
void FSPanelPreferenceSounds::onMoapInteractionChanged()
|
||||
{
|
||||
const auto bitvalue = gSavedSettings.getS32("MediaFirstClickInteract");
|
||||
|
||||
mMoapInteractionAll->set(bitvalue & LLToolPie::MEDIA_FIRST_CLICK_BYPASS_MOAP_FLAG);
|
||||
mMoapInteractionAny->set((bitvalue & LLToolPie::MEDIA_FIRST_CLICK_ANY) == LLToolPie::MEDIA_FIRST_CLICK_ANY);
|
||||
mMoapInteractionHud->set(bitvalue & LLToolPie::MEDIA_FIRST_CLICK_HUD);
|
||||
mMoapInteractionOwnObjects->set(bitvalue & LLToolPie::MEDIA_FIRST_CLICK_OWN);
|
||||
mMoapInteractionGroupObjects->set(bitvalue & LLToolPie::MEDIA_FIRST_CLICK_GROUP);
|
||||
mMoapInteractionFriendObjects->set(bitvalue & LLToolPie::MEDIA_FIRST_CLICK_FRIEND);
|
||||
mMoapInteractionLandownerObjects->set(bitvalue & LLToolPie::MEDIA_FIRST_CLICK_LAND);
|
||||
|
||||
const bool is_all_selected = (bitvalue & LLToolPie::MEDIA_FIRST_CLICK_BYPASS_MOAP_FLAG) == LLToolPie::MEDIA_FIRST_CLICK_BYPASS_MOAP_FLAG;
|
||||
const bool is_any_selected = (bitvalue & LLToolPie::MEDIA_FIRST_CLICK_ANY) == LLToolPie::MEDIA_FIRST_CLICK_ANY;
|
||||
mMoapInteractionAny->setEnabled(!is_all_selected);
|
||||
mMoapInteractionHud->setEnabled(!is_all_selected && !is_any_selected);
|
||||
mMoapInteractionOwnObjects->setEnabled(!is_all_selected && !is_any_selected);
|
||||
mMoapInteractionGroupObjects->setEnabled(!is_all_selected && !is_any_selected);
|
||||
mMoapInteractionFriendObjects->setEnabled(!is_all_selected && !is_any_selected);
|
||||
mMoapInteractionLandownerObjects->setEnabled(!is_all_selected && !is_any_selected);
|
||||
}
|
||||
|
||||
void FSPanelPreferenceSounds::updateMoapInteractionSetting()
|
||||
{
|
||||
if (mMoapInteractionAll->get())
|
||||
{
|
||||
gSavedSettings.setS32("MediaFirstClickInteract", LLToolPie::MEDIA_FIRST_CLICK_BYPASS_MOAP_FLAG | LLToolPie::MEDIA_FIRST_CLICK_ANY);
|
||||
}
|
||||
else if (mMoapInteractionAny->get())
|
||||
{
|
||||
gSavedSettings.setS32("MediaFirstClickInteract", LLToolPie::MEDIA_FIRST_CLICK_ANY);
|
||||
}
|
||||
else
|
||||
{
|
||||
const S32 value = (mMoapInteractionHud->get() ? LLToolPie::MEDIA_FIRST_CLICK_HUD : 0) |
|
||||
(mMoapInteractionOwnObjects->get() ? LLToolPie::MEDIA_FIRST_CLICK_OWN : 0) |
|
||||
(mMoapInteractionGroupObjects->get() ? LLToolPie::MEDIA_FIRST_CLICK_GROUP : 0) |
|
||||
(mMoapInteractionFriendObjects->get() ? LLToolPie::MEDIA_FIRST_CLICK_FRIEND : 0) |
|
||||
(mMoapInteractionLandownerObjects->get() ? LLToolPie::MEDIA_FIRST_CLICK_LAND : 0);
|
||||
|
||||
gSavedSettings.setS32("MediaFirstClickInteract", value);
|
||||
}
|
||||
}
|
||||
// </FS:Ansariel>
|
||||
|
|
|
|||
|
|
@ -641,9 +641,21 @@ private:
|
|||
LLPanel* mOutputDevicePanel;
|
||||
LLComboBox* mOutputDeviceComboBox;
|
||||
|
||||
LLCheckBoxCtrl* mMoapInteractionAll;
|
||||
LLCheckBoxCtrl* mMoapInteractionAny;
|
||||
LLCheckBoxCtrl* mMoapInteractionHud;
|
||||
LLCheckBoxCtrl* mMoapInteractionOwnObjects;
|
||||
LLCheckBoxCtrl* mMoapInteractionGroupObjects;
|
||||
LLCheckBoxCtrl* mMoapInteractionFriendObjects;
|
||||
LLCheckBoxCtrl* mMoapInteractionLandownerObjects;
|
||||
|
||||
void onOutputDeviceChanged(const LLSD& new_value);
|
||||
void onOutputDeviceSelectionChanged(const LLSD& new_value);
|
||||
void onOutputDeviceListChanged(LLAudioEngine::output_device_map_t output_devices);
|
||||
|
||||
void onMoapInteractionChanged();
|
||||
void updateMoapInteractionSetting();
|
||||
|
||||
boost::signals2::connection mOutputDeviceListChangedConnection;
|
||||
|
||||
LOG_CLASS(FSPanelPreferenceSounds);
|
||||
|
|
|
|||
|
|
@ -2190,7 +2190,7 @@ void LLPanelRegionTerrainInfo::initMaterialCtrl(LLTextureCtrl*& ctrl, const std:
|
|||
if (!ctrl) return;
|
||||
|
||||
// consume cancel events, otherwise they will trigger commit callbacks
|
||||
ctrl->setOnCancelCallback([](LLUICtrl* ctrl, const LLSD& param) {});
|
||||
ctrl->setOnCancelCallback(+[](LLUICtrl* ctrl, const LLSD& param) {}); // <FS:Beq/> force the empty callback to decay to a function pointer, which triggers warnings on gcc
|
||||
ctrl->setCommitCallback(
|
||||
[this, index](LLUICtrl* ctrl, const LLSD& param)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1088,7 +1088,9 @@ void LLFloaterUIPreview::getExecutablePath(const std::vector<std::string>& filen
|
|||
{
|
||||
CFStringRef executable_cfstr = (CFStringRef)CFDictionaryGetValue(bundleInfoDict, CFSTR("CFBundleExecutable")); // get the name of the actual executable (e.g. TextEdit or firefox-bin)
|
||||
int max_file_length = 256; // (max file name length is 255 in OSX)
|
||||
char executable_buf[max_file_length];
|
||||
|
||||
// Xcode 26: VLAs are a clang extension. Just create the buffer and delete it after.
|
||||
char *executable_buf = new char [max_file_length];
|
||||
if(CFStringGetCString(executable_cfstr, executable_buf, max_file_length, kCFStringEncodingMacRoman)) // convert CFStringRef to char*
|
||||
{
|
||||
executable_path += std::string("/Contents/MacOS/") + std::string(executable_buf); // append path to executable directory and then executable name to exec path
|
||||
|
|
@ -1098,6 +1100,7 @@ void LLFloaterUIPreview::getExecutablePath(const std::vector<std::string>& filen
|
|||
std::string warning = "Unable to get CString from CFString for executable path";
|
||||
popupAndPrintWarning(warning);
|
||||
}
|
||||
delete [] executable_buf;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -343,11 +343,9 @@ LLFloaterWorldMap::LLFloaterWorldMap(const LLSD& key)
|
|||
mWaitingForTracker(false),
|
||||
mIsClosing(false),
|
||||
mSetToUserPosition(true),
|
||||
mProcessingSearchUpdate(false),
|
||||
mTrackedLocation(0.0,0.0,0.0),
|
||||
mTrackedStatus(LLTracker::TRACKING_NOTHING),
|
||||
mListFriendCombo(nullptr),
|
||||
mListLandmarkCombo(nullptr),
|
||||
mListSearchResults(nullptr),
|
||||
mParcelInfoObserver(nullptr),
|
||||
mShowParcelInfo(false)
|
||||
{
|
||||
|
|
@ -359,7 +357,7 @@ LLFloaterWorldMap::LLFloaterWorldMap(const LLSD& key)
|
|||
mCommitCallbackRegistrar.add("WMap.Location", boost::bind(&LLFloaterWorldMap::onLocationCommit, this));
|
||||
mCommitCallbackRegistrar.add("WMap.AvatarCombo", boost::bind(&LLFloaterWorldMap::onAvatarComboCommit, this));
|
||||
mCommitCallbackRegistrar.add("WMap.Landmark", boost::bind(&LLFloaterWorldMap::onLandmarkComboCommit, this));
|
||||
mCommitCallbackRegistrar.add("WMap.SearchResult", boost::bind(&LLFloaterWorldMap::onCommitSearchResult, this));
|
||||
mCommitCallbackRegistrar.add("WMap.SearchResult", [this](LLUICtrl* ctrl, const LLSD& data) { LLFloaterWorldMap::onCommitSearchResult(false); });
|
||||
mCommitCallbackRegistrar.add("WMap.GoHome", boost::bind(&LLFloaterWorldMap::onGoHome, this));
|
||||
mCommitCallbackRegistrar.add("WMap.Teleport", boost::bind(&LLFloaterWorldMap::onClickTeleportBtn, this));
|
||||
mCommitCallbackRegistrar.add("WMap.ShowTarget", boost::bind(&LLFloaterWorldMap::onShowTargetBtn, this));
|
||||
|
|
@ -410,24 +408,22 @@ bool LLFloaterWorldMap::postBuild()
|
|||
mTeleportCoordSpinZ = getChild<LLSpinCtrl>("teleport_coordinate_z");
|
||||
// </FS>
|
||||
|
||||
LLComboBox *avatar_combo = getChild<LLComboBox>("friend combo");
|
||||
avatar_combo->selectFirstItem();
|
||||
avatar_combo->setPrearrangeCallback( boost::bind(&LLFloaterWorldMap::onAvatarComboPrearrange, this) );
|
||||
avatar_combo->setTextChangedCallback( boost::bind(&LLFloaterWorldMap::onComboTextEntry, this) );
|
||||
mListFriendCombo = dynamic_cast<LLCtrlListInterface *>(avatar_combo);
|
||||
mFriendCombo = getChild<LLComboBox>("friend combo");
|
||||
mFriendCombo->selectFirstItem();
|
||||
mFriendCombo->setPrearrangeCallback(boost::bind(&LLFloaterWorldMap::onAvatarComboPrearrange, this));
|
||||
mFriendCombo->setTextChangedCallback(boost::bind(&LLFloaterWorldMap::onComboTextEntry, this));
|
||||
|
||||
mLocationEditor = getChild<LLSearchEditor>("location");
|
||||
mLocationEditor->setFocusChangedCallback(boost::bind(&LLFloaterWorldMap::onLocationFocusChanged, this, _1));
|
||||
mLocationEditor->setTextChangedCallback( boost::bind(&LLFloaterWorldMap::onSearchTextEntry, this));
|
||||
mLocationEditor->setTextChangedCallback(boost::bind(&LLFloaterWorldMap::onSearchTextEntry, this));
|
||||
|
||||
getChild<LLScrollListCtrl>("search_results")->setDoubleClickCallback( boost::bind(&LLFloaterWorldMap::onClickTeleportBtn, this));
|
||||
mListSearchResults = childGetListInterface("search_results");
|
||||
mSearchResults = getChild<LLScrollListCtrl>("search_results");
|
||||
mSearchResults->setDoubleClickCallback(boost::bind(&LLFloaterWorldMap::onClickTeleportBtn, this));
|
||||
|
||||
LLComboBox *landmark_combo = getChild<LLComboBox>( "landmark combo");
|
||||
landmark_combo->selectFirstItem();
|
||||
landmark_combo->setPrearrangeCallback( boost::bind(&LLFloaterWorldMap::onLandmarkComboPrearrange, this) );
|
||||
landmark_combo->setTextChangedCallback( boost::bind(&LLFloaterWorldMap::onComboTextEntry, this) );
|
||||
mListLandmarkCombo = dynamic_cast<LLCtrlListInterface *>(landmark_combo);
|
||||
mLandmarkCombo = getChild<LLComboBox>("landmark combo");
|
||||
mLandmarkCombo->selectFirstItem();
|
||||
mLandmarkCombo->setPrearrangeCallback(boost::bind(&LLFloaterWorldMap::onLandmarkComboPrearrange, this));
|
||||
mLandmarkCombo->setTextChangedCallback(boost::bind(&LLFloaterWorldMap::onComboTextEntry, this));
|
||||
|
||||
mZoomSlider = getChild<LLSliderCtrl>("zoom slider");
|
||||
F32 slider_zoom = mMapView->getZoom();
|
||||
|
|
@ -662,7 +658,6 @@ void LLFloaterWorldMap::draw()
|
|||
// (!rlv_handler_t::isEnabled()) || !(gRlvHandler.hasBehaviour(RLV_BHVR_TPLM) && gRlvHandler.hasBehaviour(RLV_BHVR_TPLOC)));
|
||||
//// [/RLVa:KB]
|
||||
mTeleportButton->setEnabled((bool)tracking_status);
|
||||
//clear_btn->setEnabled((bool)tracking_status);
|
||||
mShowDestinationButton->setEnabled((bool)tracking_status || LLWorldMap::getInstance()->isTracking());
|
||||
mCopySlurlButton->setEnabled((mSLURL.isValid()) );
|
||||
mGoHomeButton->setEnabled((!rlv_handler_t::isEnabled()) || !(gRlvHandler.hasBehaviour(RLV_BHVR_TPLM) && gRlvHandler.hasBehaviour(RLV_BHVR_TPLOC)));
|
||||
|
|
@ -758,26 +753,24 @@ void LLFloaterWorldMap::requestParcelInfo(const LLVector3d& pos_global, const LL
|
|||
}
|
||||
}
|
||||
|
||||
void LLFloaterWorldMap::trackAvatar( const LLUUID& avatar_id, const std::string& name )
|
||||
void LLFloaterWorldMap::trackAvatar(const LLUUID& avatar_id, const std::string& name)
|
||||
{
|
||||
mShowParcelInfo = false;
|
||||
LLCtrlSelectionInterface *iface = childGetSelectionInterface("friend combo");
|
||||
if (!iface) return;
|
||||
|
||||
buildAvatarIDList();
|
||||
if(iface->setCurrentByID(avatar_id) || gAgent.isGodlike())
|
||||
if (mFriendCombo->setCurrentByID(avatar_id) || gAgent.isGodlike())
|
||||
{
|
||||
// *HACK: Adjust Z values automatically for liaisons & gods so
|
||||
// they swoop down when they click on the map. Requested
|
||||
// convenience.
|
||||
if(gAgent.isGodlike())
|
||||
if (gAgent.isGodlike())
|
||||
{
|
||||
mTeleportCoordSpinZ->setValue(LLSD(200.f));
|
||||
}
|
||||
// Don't re-request info if we already have it or we won't have it in time to teleport
|
||||
if (mTrackedStatus != LLTracker::TRACKING_AVATAR || avatar_id != mTrackedAvatarID)
|
||||
{
|
||||
mTrackedStatus = LLTracker::TRACKING_AVATAR;
|
||||
mTrackedStatus = LLTracker::TRACKING_AVATAR;
|
||||
mTrackedAvatarID = avatar_id;
|
||||
LLTracker::trackAvatar(avatar_id, name);
|
||||
centerOnTarget(true);
|
||||
|
|
@ -790,43 +783,36 @@ void LLFloaterWorldMap::trackAvatar( const LLUUID& avatar_id, const std::string&
|
|||
mTrackCtrlsPanel->setDefaultBtn(mTeleportButton);
|
||||
}
|
||||
|
||||
void LLFloaterWorldMap::trackLandmark( const LLUUID& landmark_item_id )
|
||||
void LLFloaterWorldMap::trackLandmark(const LLUUID& landmark_item_id)
|
||||
{
|
||||
mShowParcelInfo = false;
|
||||
LLCtrlSelectionInterface *iface = childGetSelectionInterface("landmark combo");
|
||||
if (!iface) return;
|
||||
|
||||
buildLandmarkIDLists();
|
||||
bool found = false;
|
||||
S32 idx;
|
||||
S32 idx;
|
||||
for (idx = 0; idx < mLandmarkItemIDList.size(); idx++)
|
||||
{
|
||||
if ( mLandmarkItemIDList.at(idx) == landmark_item_id)
|
||||
if (mLandmarkItemIDList.at(idx) == landmark_item_id)
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found && iface->setCurrentByID( landmark_item_id ) )
|
||||
if (found && mLandmarkCombo->setCurrentByID(landmark_item_id))
|
||||
{
|
||||
LLUUID asset_id = mLandmarkAssetIDList.at( idx );
|
||||
std::string name;
|
||||
LLComboBox* combo = getChild<LLComboBox>( "landmark combo");
|
||||
if (combo) name = combo->getSimple();
|
||||
mTrackedStatus = LLTracker::TRACKING_LANDMARK;
|
||||
LLTracker::trackLandmark(mLandmarkAssetIDList.at( idx ), // assetID
|
||||
mLandmarkItemIDList.at( idx ), // itemID
|
||||
name); // name
|
||||
LLUUID asset_id = mLandmarkAssetIDList.at(idx);
|
||||
std::string name = mLandmarkCombo->getSimple();
|
||||
mTrackedStatus = LLTracker::TRACKING_LANDMARK;
|
||||
LLTracker::trackLandmark(mLandmarkAssetIDList.at(idx), // assetID
|
||||
mLandmarkItemIDList.at(idx), // itemID
|
||||
name); // name
|
||||
|
||||
if( asset_id != sHomeID )
|
||||
if (asset_id != sHomeID)
|
||||
{
|
||||
// start the download process
|
||||
gLandmarkList.getAsset( asset_id);
|
||||
gLandmarkList.getAsset(asset_id);
|
||||
}
|
||||
|
||||
// We have to download both region info and landmark data, so set busy. JC
|
||||
// getWindow()->incBusyCount();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -854,6 +840,7 @@ void LLFloaterWorldMap::trackGenericItem(const LLItemInfo &item)
|
|||
|
||||
void LLFloaterWorldMap::trackLocation(const LLVector3d& pos_global)
|
||||
{
|
||||
mProcessingSearchUpdate = false;
|
||||
LLSimInfo* sim_info = LLWorldMap::getInstance()->simInfoFromPosGlobal(pos_global);
|
||||
if (!sim_info)
|
||||
{
|
||||
|
|
@ -1065,7 +1052,10 @@ void LLFloaterWorldMap::updateLocation()
|
|||
}
|
||||
}
|
||||
|
||||
mLocationEditor->setValue(sim_name);
|
||||
if (!mProcessingSearchUpdate)
|
||||
{
|
||||
mLocationEditor->setValue(sim_name);
|
||||
}
|
||||
|
||||
// refresh coordinate display to reflect where user clicked.
|
||||
LLVector3d coord_pos = LLTracker::getTrackedPositionGlobal();
|
||||
|
|
@ -1195,17 +1185,14 @@ void LLFloaterWorldMap::observeFriends()
|
|||
|
||||
void LLFloaterWorldMap::friendsChanged()
|
||||
{
|
||||
LLAvatarTracker& t = LLAvatarTracker::instance();
|
||||
const LLUUID& avatar_id = t.getAvatarID();
|
||||
LLAvatarTracker& t = LLAvatarTracker::instance();
|
||||
const LLUUID& avatar_id = t.getAvatarID();
|
||||
buildAvatarIDList();
|
||||
if(avatar_id.notNull())
|
||||
if (avatar_id.notNull())
|
||||
{
|
||||
LLCtrlSelectionInterface *iface = childGetSelectionInterface("friend combo");
|
||||
const LLRelationship* buddy_info = t.getBuddyInfo(avatar_id);
|
||||
if(!iface ||
|
||||
!iface->setCurrentByID(avatar_id) ||
|
||||
(buddy_info && !buddy_info->isRightGrantedFrom(LLRelationship::GRANT_MAP_LOCATION)) ||
|
||||
gAgent.isGodlike())
|
||||
if (!mFriendCombo->setCurrentByID(avatar_id) ||
|
||||
(buddy_info && !buddy_info->isRightGrantedFrom(LLRelationship::GRANT_MAP_LOCATION)) || gAgent.isGodlike())
|
||||
{
|
||||
LLTracker::stopTracking(false);
|
||||
}
|
||||
|
|
@ -1215,15 +1202,12 @@ void LLFloaterWorldMap::friendsChanged()
|
|||
// No longer really builds a list. Instead, just updates mAvatarCombo.
|
||||
void LLFloaterWorldMap::buildAvatarIDList()
|
||||
{
|
||||
LLCtrlListInterface *list = mListFriendCombo;
|
||||
if (!list) return;
|
||||
|
||||
// Delete all but the "None" entry
|
||||
S32 list_size = list->getItemCount();
|
||||
S32 list_size = mFriendCombo->getItemCount();
|
||||
if (list_size > 1)
|
||||
{
|
||||
list->selectItemRange(1, -1);
|
||||
list->operateOnSelection(LLCtrlListInterface::OP_DELETE);
|
||||
mFriendCombo->selectItemRange(1, -1);
|
||||
mFriendCombo->operateOnSelection(LLCtrlListInterface::OP_DELETE);
|
||||
}
|
||||
|
||||
// Get all of the calling cards for avatar that are currently online
|
||||
|
|
@ -1231,41 +1215,38 @@ void LLFloaterWorldMap::buildAvatarIDList()
|
|||
LLAvatarTracker::instance().applyFunctor(collector);
|
||||
LLCollectMappableBuddies::buddy_map_t::iterator it;
|
||||
LLCollectMappableBuddies::buddy_map_t::iterator end;
|
||||
it = collector.mMappable.begin();
|
||||
it = collector.mMappable.begin();
|
||||
end = collector.mMappable.end();
|
||||
// <FS:Ansariel> Sort friend list alphabetically
|
||||
//for( ; it != end; ++it)
|
||||
//for(; it != end; ++it)
|
||||
//{
|
||||
// list->addSimpleElement((*it).second, ADD_BOTTOM, (*it).first);
|
||||
// mFriendCombo->addSimpleElement((*it).second, ADD_BOTTOM, (*it).first);
|
||||
//}
|
||||
|
||||
std::multimap<std::string, LLUUID> buddymap;
|
||||
for( ; it != end; ++it)
|
||||
for(; it != end; ++it)
|
||||
{
|
||||
buddymap.insert(std::make_pair((*it).second, (*it).first));
|
||||
}
|
||||
for (std::multimap<std::string, LLUUID>::iterator bit = buddymap.begin(); bit != buddymap.end(); ++bit)
|
||||
{
|
||||
list->addSimpleElement((*bit).first, ADD_BOTTOM, (*bit).second);
|
||||
mFriendCombo->addSimpleElement((*bit).first, ADD_BOTTOM, (*bit).second);
|
||||
}
|
||||
// </FS:Ansariel>
|
||||
|
||||
list->setCurrentByID( LLAvatarTracker::instance().getAvatarID() );
|
||||
list->selectFirstItem();
|
||||
mFriendCombo->setCurrentByID(LLAvatarTracker::instance().getAvatarID());
|
||||
mFriendCombo->selectFirstItem();
|
||||
}
|
||||
|
||||
|
||||
void LLFloaterWorldMap::buildLandmarkIDLists()
|
||||
{
|
||||
LLCtrlListInterface *list = mListLandmarkCombo;
|
||||
if (!list) return;
|
||||
|
||||
// Delete all but the "None" entry
|
||||
S32 list_size = list->getItemCount();
|
||||
S32 list_size = mLandmarkCombo->getItemCount();
|
||||
if (list_size > 1)
|
||||
{
|
||||
list->selectItemRange(1, -1);
|
||||
list->operateOnSelection(LLCtrlListInterface::OP_DELETE);
|
||||
mLandmarkCombo->selectItemRange(1, -1);
|
||||
mLandmarkCombo->operateOnSelection(LLCtrlListInterface::OP_DELETE);
|
||||
}
|
||||
|
||||
mLandmarkItemIDList.clear();
|
||||
|
|
@ -1312,13 +1293,13 @@ void LLFloaterWorldMap::buildLandmarkIDLists()
|
|||
}
|
||||
// </FS:Ansariel>
|
||||
|
||||
list->addSimpleElement(item->getName(), ADD_BOTTOM, item->getUUID());
|
||||
mLandmarkCombo->addSimpleElement(item->getName(), ADD_BOTTOM, item->getUUID());
|
||||
|
||||
mLandmarkAssetIDList.push_back( item->getAssetUUID() );
|
||||
mLandmarkItemIDList.push_back( item->getUUID() );
|
||||
}
|
||||
|
||||
list->selectFirstItem();
|
||||
mLandmarkCombo->selectFirstItem();
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1336,10 +1317,9 @@ F32 LLFloaterWorldMap::getDistanceToDestination(const LLVector3d &destination,
|
|||
|
||||
void LLFloaterWorldMap::clearLocationSelection(bool clear_ui, bool dest_reached)
|
||||
{
|
||||
LLCtrlListInterface *list = mListSearchResults;
|
||||
if (list && (!dest_reached || (list->getItemCount() == 1)))
|
||||
if (!dest_reached || (mSearchResults->getItemCount() == 1))
|
||||
{
|
||||
list->operateOnAll(LLCtrlListInterface::OP_DELETE);
|
||||
mSearchResults->operateOnAll(LLCtrlListInterface::OP_DELETE);
|
||||
}
|
||||
LLWorldMap::getInstance()->cancelTracking();
|
||||
mCompletingRegionName = "";
|
||||
|
|
@ -1350,11 +1330,7 @@ void LLFloaterWorldMap::clearLandmarkSelection(bool clear_ui)
|
|||
{
|
||||
if (clear_ui || !childHasKeyboardFocus("landmark combo"))
|
||||
{
|
||||
LLCtrlListInterface *list = mListLandmarkCombo;
|
||||
if (list)
|
||||
{
|
||||
list->selectByValue( "None" );
|
||||
}
|
||||
mLandmarkCombo->selectByValue("None");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1364,10 +1340,9 @@ void LLFloaterWorldMap::clearAvatarSelection(bool clear_ui)
|
|||
if (clear_ui || !childHasKeyboardFocus("friend combo"))
|
||||
{
|
||||
mTrackedStatus = LLTracker::TRACKING_NOTHING;
|
||||
LLCtrlListInterface *list = mListFriendCombo;
|
||||
if (list && list->getSelectedValue().asString() != "None")
|
||||
if (mFriendCombo->getSelectedValue().asString() != "None")
|
||||
{
|
||||
list->selectByValue( "None" );
|
||||
mFriendCombo->selectByValue("None");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1420,28 +1395,25 @@ void LLFloaterWorldMap::onGoHome()
|
|||
{
|
||||
gAgent.teleportHome();
|
||||
closeFloater();
|
||||
mProcessingSearchUpdate = false;
|
||||
}
|
||||
|
||||
|
||||
void LLFloaterWorldMap::onLandmarkComboPrearrange( )
|
||||
void LLFloaterWorldMap::onLandmarkComboPrearrange()
|
||||
{
|
||||
if( mIsClosing )
|
||||
if (mIsClosing)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LLCtrlListInterface *list = mListLandmarkCombo;
|
||||
if (!list) return;
|
||||
|
||||
LLUUID current_choice = list->getCurrentID();
|
||||
LLUUID current_choice = mLandmarkCombo->getCurrentID();
|
||||
|
||||
buildLandmarkIDLists();
|
||||
|
||||
if( current_choice.isNull() || !list->setCurrentByID( current_choice ) )
|
||||
if (current_choice.isNull() || !mLandmarkCombo->setCurrentByID(current_choice))
|
||||
{
|
||||
LLTracker::stopTracking(false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void LLFloaterWorldMap::onComboTextEntry()
|
||||
|
|
@ -1461,33 +1433,28 @@ void LLFloaterWorldMap::onSearchTextEntry( )
|
|||
|
||||
void LLFloaterWorldMap::onLandmarkComboCommit()
|
||||
{
|
||||
if( mIsClosing )
|
||||
if (mIsClosing)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LLCtrlListInterface *list = mListLandmarkCombo;
|
||||
if (!list) return;
|
||||
|
||||
LLUUID asset_id;
|
||||
LLUUID item_id = list->getCurrentID();
|
||||
LLUUID item_id = mLandmarkCombo->getCurrentID();
|
||||
|
||||
LLTracker::stopTracking(false);
|
||||
|
||||
//RN: stopTracking() clears current combobox selection, need to reassert it here
|
||||
list->setCurrentByID(item_id);
|
||||
// RN: stopTracking() clears current combobox selection, need to reassert it here
|
||||
mLandmarkCombo->setCurrentByID(item_id);
|
||||
|
||||
if( item_id.isNull() )
|
||||
{
|
||||
}
|
||||
else if( item_id == sHomeID )
|
||||
if (item_id.isNull()) {}
|
||||
else if (item_id == sHomeID)
|
||||
{
|
||||
asset_id = sHomeID;
|
||||
}
|
||||
else
|
||||
{
|
||||
LLInventoryItem* item = gInventory.getItem( item_id );
|
||||
if( item )
|
||||
LLInventoryItem* item = gInventory.getItem(item_id);
|
||||
if (item)
|
||||
{
|
||||
asset_id = item->getAssetUUID();
|
||||
}
|
||||
|
|
@ -1498,34 +1465,31 @@ void LLFloaterWorldMap::onLandmarkComboCommit()
|
|||
}
|
||||
}
|
||||
|
||||
trackLandmark( item_id);
|
||||
trackLandmark(item_id);
|
||||
onShowTargetBtn();
|
||||
|
||||
// Reset to user postion if nothing is tracked
|
||||
mSetToUserPosition = ( LLTracker::getTrackingStatus() == LLTracker::TRACKING_NOTHING );
|
||||
mSetToUserPosition = (LLTracker::getTrackingStatus() == LLTracker::TRACKING_NOTHING);
|
||||
}
|
||||
|
||||
// static
|
||||
void LLFloaterWorldMap::onAvatarComboPrearrange( )
|
||||
void LLFloaterWorldMap::onAvatarComboPrearrange()
|
||||
{
|
||||
if( mIsClosing )
|
||||
if (mIsClosing)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LLCtrlListInterface *list = mListFriendCombo;
|
||||
if (!list) return;
|
||||
|
||||
LLUUID current_choice;
|
||||
|
||||
if( LLAvatarTracker::instance().haveTrackingInfo() )
|
||||
if (LLAvatarTracker::instance().haveTrackingInfo())
|
||||
{
|
||||
current_choice = LLAvatarTracker::instance().getAvatarID();
|
||||
}
|
||||
|
||||
buildAvatarIDList();
|
||||
|
||||
if( !list->setCurrentByID( current_choice ) || current_choice.isNull() )
|
||||
if (!mFriendCombo->setCurrentByID(current_choice) || current_choice.isNull())
|
||||
{
|
||||
LLTracker::stopTracking(false);
|
||||
}
|
||||
|
|
@ -1533,26 +1497,21 @@ void LLFloaterWorldMap::onAvatarComboPrearrange( )
|
|||
|
||||
void LLFloaterWorldMap::onAvatarComboCommit()
|
||||
{
|
||||
if( mIsClosing )
|
||||
if (mIsClosing)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LLCtrlListInterface *list = mListFriendCombo;
|
||||
if (!list) return;
|
||||
|
||||
const LLUUID& new_avatar_id = list->getCurrentID();
|
||||
const LLUUID& new_avatar_id = mFriendCombo->getCurrentID();
|
||||
if (new_avatar_id.notNull())
|
||||
{
|
||||
std::string name;
|
||||
LLComboBox* combo = getChild<LLComboBox>("friend combo");
|
||||
if (combo) name = combo->getSimple();
|
||||
std::string name = mFriendCombo->getSimple();
|
||||
trackAvatar(new_avatar_id, name);
|
||||
onShowTargetBtn();
|
||||
}
|
||||
else
|
||||
{ // Reset to user postion if nothing is tracked
|
||||
mSetToUserPosition = ( LLTracker::getTrackingStatus() == LLTracker::TRACKING_NOTHING );
|
||||
{ // Reset to user postion if nothing is tracked
|
||||
mSetToUserPosition = (LLTracker::getTrackingStatus() == LLTracker::TRACKING_NOTHING);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1606,6 +1565,7 @@ void LLFloaterWorldMap::onLocationCommit()
|
|||
{
|
||||
return;
|
||||
}
|
||||
mProcessingSearchUpdate = true;
|
||||
|
||||
LLStringUtil::toLower(str);
|
||||
mCompletingRegionName = str;
|
||||
|
|
@ -1627,6 +1587,7 @@ void LLFloaterWorldMap::onCoordinatesCommit()
|
|||
{
|
||||
return;
|
||||
}
|
||||
mProcessingSearchUpdate = false;
|
||||
|
||||
S32 x_coord = (S32)mTeleportCoordSpinX->getValue().asReal();
|
||||
S32 y_coord = (S32)mTeleportCoordSpinY->getValue().asReal();
|
||||
|
|
@ -1640,6 +1601,7 @@ void LLFloaterWorldMap::onCoordinatesCommit()
|
|||
void LLFloaterWorldMap::onClearBtn()
|
||||
{
|
||||
mTrackedStatus = LLTracker::TRACKING_NOTHING;
|
||||
mProcessingSearchUpdate = false;
|
||||
LLTracker::stopTracking(true);
|
||||
LLWorldMap::getInstance()->cancelTracking();
|
||||
mSLURL = LLSLURL(); // Clear the SLURL since it's invalid
|
||||
|
|
@ -1656,6 +1618,7 @@ void LLFloaterWorldMap::onShowAgentBtn()
|
|||
mMapView->setPanWithInterpTime(0, 0, false, 0.1f); // false == animate
|
||||
// Set flag so user's location will be displayed if not tracking anything else
|
||||
mSetToUserPosition = true;
|
||||
mProcessingSearchUpdate = false;
|
||||
}
|
||||
|
||||
void LLFloaterWorldMap::onClickTeleportBtn()
|
||||
|
|
@ -1685,8 +1648,9 @@ void LLFloaterWorldMap::onCopySLURL()
|
|||
|
||||
// std::string image_name = getString(toggle_collapse ? "expand_icon" : "collapse_icon");
|
||||
// std::string tooltip = getString(toggle_collapse ? "expand_tooltip" : "collapse_tooltip");
|
||||
// getChild<LLIconCtrl>("expand_collapse_icon")->setImage(LLUI::getUIImage(image_name));
|
||||
// getChild<LLIconCtrl>("expand_collapse_icon")->setToolTip(tooltip);
|
||||
// LLIconCtrl* expandCollapseIcon = getChild<LLIconCtrl>("expand_collapse_icon");
|
||||
// expandCollapseIcon->setImage(LLUI::getUIImage(image_name));
|
||||
// expandCollapseIcon->setToolTip(tooltip);
|
||||
// getChild<LLPanel>("expand_btn_panel")->setToolTip(tooltip);
|
||||
//}
|
||||
// </FS:Ansariel>
|
||||
|
|
@ -1844,6 +1808,12 @@ void LLFloaterWorldMap::teleport()
|
|||
gAgent.teleportViaLocation( pos_global );
|
||||
}
|
||||
}
|
||||
|
||||
if (mProcessingSearchUpdate)
|
||||
{
|
||||
mProcessingSearchUpdate = false;
|
||||
mTrackedSimName.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void LLFloaterWorldMap::flyToLandmark()
|
||||
|
|
@ -1911,9 +1881,9 @@ void LLFloaterWorldMap::teleportToAvatar()
|
|||
|
||||
void LLFloaterWorldMap::flyToAvatar()
|
||||
{
|
||||
if( LLAvatarTracker::instance().haveTrackingInfo() )
|
||||
if (LLAvatarTracker::instance().haveTrackingInfo())
|
||||
{
|
||||
gAgent.startAutoPilotGlobal( LLAvatarTracker::instance().getGlobalPos() );
|
||||
gAgent.startAutoPilotGlobal(LLAvatarTracker::instance().getGlobalPos());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1924,8 +1894,7 @@ void LLFloaterWorldMap::updateSims(bool found_null_sim)
|
|||
return;
|
||||
}
|
||||
|
||||
LLScrollListCtrl *list = getChild<LLScrollListCtrl>("search_results");
|
||||
list->operateOnAll(LLCtrlListInterface::OP_DELETE);
|
||||
mSearchResults->operateOnAll(LLCtrlListInterface::OP_DELETE);
|
||||
|
||||
// auto name_length = mCompletingRegionName.length(); // <FS:Beq/> FIRE-23591 support map search partial matches (Patch by Kevin Cozens)
|
||||
|
||||
|
|
@ -1955,7 +1924,7 @@ void LLFloaterWorldMap::updateSims(bool found_null_sim)
|
|||
value["id"] = info->getName();
|
||||
value["columns"][0]["column"] = "sim_name";
|
||||
value["columns"][0]["value"] = info->getName();
|
||||
list->addElement(value);
|
||||
mSearchResults->addElement(value);
|
||||
num_results++;
|
||||
}
|
||||
}
|
||||
|
|
@ -1968,26 +1937,29 @@ void LLFloaterWorldMap::updateSims(bool found_null_sim)
|
|||
if (num_results > 0)
|
||||
{
|
||||
// Ansariel: Let's sort the list to make it more user-friendly
|
||||
list->sortByColumn("sim_name", true);
|
||||
mSearchResults->sortByColumn("sim_name", true);
|
||||
|
||||
// if match found, highlight it and go
|
||||
if (!match.isUndefined())
|
||||
{
|
||||
list->selectByValue(match);
|
||||
mSearchResults->selectByValue(match);
|
||||
mSearchResults->setFocus(true);
|
||||
onCommitSearchResult(false /*fully commit the only option*/);
|
||||
}
|
||||
// else select first found item
|
||||
// else let user decide
|
||||
else
|
||||
{
|
||||
list->selectFirstItem();
|
||||
mSearchResults->selectFirstItem();
|
||||
mSearchResults->setFocus(true);
|
||||
onCommitSearchResult(true /*don't update text field*/);
|
||||
}
|
||||
getChild<LLUICtrl>("search_results")->setFocus(true);
|
||||
onCommitSearchResult();
|
||||
}
|
||||
else
|
||||
{
|
||||
// if we found nothing, say "none"
|
||||
list->setCommentText(LLTrans::getString("worldmap_results_none_found"));
|
||||
list->operateOnAll(LLCtrlListInterface::OP_DESELECT);
|
||||
mProcessingSearchUpdate = false;
|
||||
mSearchResults->setCommentText(LLTrans::getString("worldmap_results_none_found"));
|
||||
mSearchResults->operateOnAll(LLCtrlListInterface::OP_DESELECT);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1999,13 +1971,9 @@ void LLFloaterWorldMap::onTeleportFinished()
|
|||
}
|
||||
}
|
||||
|
||||
void LLFloaterWorldMap::onCommitSearchResult()
|
||||
void LLFloaterWorldMap::onCommitSearchResult(bool from_search)
|
||||
{
|
||||
LLCtrlListInterface *list = mListSearchResults;
|
||||
if (!list) return;
|
||||
|
||||
LLSD selected_value = list->getSelectedValue();
|
||||
std::string sim_name = selected_value.asString();
|
||||
std::string sim_name = mSearchResults->getSelectedValue().asString();
|
||||
if (sim_name.empty())
|
||||
{
|
||||
return;
|
||||
|
|
@ -2021,7 +1989,7 @@ void LLFloaterWorldMap::onCommitSearchResult()
|
|||
{
|
||||
LLVector3d pos_global = info->getGlobalOrigin();
|
||||
|
||||
const F64 SIM_COORD_DEFAULT = 128.0;
|
||||
constexpr F64 SIM_COORD_DEFAULT = 128.0;
|
||||
LLVector3 pos_local(SIM_COORD_DEFAULT, SIM_COORD_DEFAULT, 0.0f);
|
||||
|
||||
// Did this value come from a trackURL() request?
|
||||
|
|
@ -2034,8 +2002,14 @@ void LLFloaterWorldMap::onCommitSearchResult()
|
|||
pos_global.mdV[VY] += (F64)pos_local.mV[VY];
|
||||
pos_global.mdV[VZ] = (F64)pos_local.mV[VZ];
|
||||
|
||||
mLocationEditor->setValue(sim_name);
|
||||
// Commiting search string automatically selects first item in the search list,
|
||||
// in such case onCommitSearchResult shouldn't modify search string
|
||||
if (!from_search)
|
||||
{
|
||||
mLocationEditor->setValue(sim_name);
|
||||
}
|
||||
trackLocation(pos_global);
|
||||
mProcessingSearchUpdate = from_search;
|
||||
mTrackCtrlsPanel->setDefaultBtn(mTeleportButton);
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,6 +54,8 @@ class LLCheckBoxCtrl;
|
|||
class LLSliderCtrl;
|
||||
class LLSpinCtrl;
|
||||
class LLSearchEditor;
|
||||
class LLComboBox;
|
||||
class LLScrollListCtrl;
|
||||
|
||||
class LLWorldMapParcelInfoObserver : public LLRemoteParcelInfoObserver
|
||||
{
|
||||
|
|
@ -180,7 +182,7 @@ protected:
|
|||
void onLocationFocusChanged( LLFocusableElement* ctrl );
|
||||
void onLocationCommit();
|
||||
void onCoordinatesCommit();
|
||||
void onCommitSearchResult();
|
||||
void onCommitSearchResult(bool from_search);
|
||||
|
||||
void onTeleportFinished();
|
||||
|
||||
|
|
@ -217,6 +219,7 @@ private:
|
|||
|
||||
bool mIsClosing;
|
||||
bool mSetToUserPosition;
|
||||
bool mProcessingSearchUpdate; // Don't update search string from what user set it to
|
||||
|
||||
LLVector3d mTrackedLocation;
|
||||
LLTracker::ETrackingStatus mTrackedStatus;
|
||||
|
|
@ -224,10 +227,6 @@ private:
|
|||
LLUUID mTrackedAvatarID;
|
||||
LLSLURL mSLURL;
|
||||
|
||||
LLCtrlListInterface * mListFriendCombo;
|
||||
LLCtrlListInterface * mListLandmarkCombo;
|
||||
LLCtrlListInterface * mListSearchResults;
|
||||
|
||||
LLButton* mTeleportButton = nullptr;
|
||||
LLButton* mShowDestinationButton = nullptr;
|
||||
LLButton* mCopySlurlButton = nullptr;
|
||||
|
|
@ -259,6 +258,11 @@ private:
|
|||
|
||||
LLSliderCtrl* mZoomSlider = nullptr;
|
||||
|
||||
LLComboBox* mLandmarkCombo = nullptr;
|
||||
LLComboBox* mFriendCombo = nullptr;
|
||||
|
||||
LLScrollListCtrl* mSearchResults = nullptr;
|
||||
|
||||
LLPanel* mTrackCtrlsPanel = nullptr;
|
||||
|
||||
boost::signals2::connection mTeleportFinishConnection;
|
||||
|
|
|
|||
|
|
@ -311,10 +311,6 @@ void LLHUDText::renderText()
|
|||
}
|
||||
|
||||
text_color = segment_iter->mColor;
|
||||
if (mOnHUDAttachment)
|
||||
{
|
||||
text_color = linearColor4(text_color);
|
||||
}
|
||||
text_color.mV[VALPHA] *= alpha_factor;
|
||||
// <FS:minerjr> [FIRE-35019] Add LLHUDNameTag background to floating text and hover highlights
|
||||
// If the text object is highlighted and use hover highlight is enabled, then reset the alpha factor (1.0f)
|
||||
|
|
|
|||
|
|
@ -1060,7 +1060,7 @@ void LLInventoryFilter::setFilterSubString(const std::string& string)
|
|||
boost::char_separator<char> sep("+");
|
||||
tokenizer tokens(filter_sub_string_new, sep);
|
||||
|
||||
for (auto token_iter : tokens)
|
||||
for (const auto& token_iter : tokens)
|
||||
{
|
||||
mFilterTokens.push_back(token_iter);
|
||||
}
|
||||
|
|
@ -1128,7 +1128,7 @@ void LLInventoryFilter::setFilterSubString(const std::string& string)
|
|||
// </FS:Zi>
|
||||
|
||||
// Cancel out UUID once the search string is modified
|
||||
if (mFilterOps.mFilterTypes == FILTERTYPE_UUID)
|
||||
if (mFilterOps.mFilterTypes & FILTERTYPE_UUID)
|
||||
{
|
||||
// <FS:Ansariel> Find all links unhiding hidden empty system folders
|
||||
//mFilterOps.mFilterTypes &= ~FILTERTYPE_UUID;
|
||||
|
|
@ -1948,7 +1948,7 @@ std::string LLInventoryFilter::getEmptyLookupMessage(bool is_empty_folder) const
|
|||
}
|
||||
}
|
||||
|
||||
bool LLInventoryFilter::areDateLimitsSet()
|
||||
bool LLInventoryFilter::areDateLimitsSet() const
|
||||
{
|
||||
return mFilterOps.mMinDate != time_min()
|
||||
|| mFilterOps.mMaxDate != time_max()
|
||||
|
|
|
|||
|
|
@ -363,7 +363,7 @@ public:
|
|||
bool checkAgainstFilterThumbnails(const LLUUID& object_id) const;
|
||||
|
||||
private:
|
||||
bool areDateLimitsSet();
|
||||
bool areDateLimitsSet() const;
|
||||
bool checkAgainstFilterSubString(const std::string& desc) const;
|
||||
bool checkAgainstFilterType(const class LLFolderViewModelItemInventory* listener) const;
|
||||
bool checkAgainstFilterType(const LLInventoryItem* item) const;
|
||||
|
|
|
|||
|
|
@ -2509,9 +2509,9 @@ bool can_move_to_my_outfits_as_outfit(LLInventoryModel* model, LLInventoryCatego
|
|||
return false;
|
||||
}
|
||||
|
||||
if (items->size() == 0)
|
||||
if (items->size() == 0 && inv_cat->getPreferredType() != LLFolderType::FT_OUTFIT)
|
||||
{
|
||||
// Nothing to move(create)
|
||||
// Nothing to create an outfit folder from
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2920,6 +2920,7 @@ bool LLInventoryModel::loadSkeleton(
|
|||
|
||||
LL_DEBUGS(LOG_INV) << "importing inventory skeleton for " << owner_id << LL_ENDL;
|
||||
|
||||
LLTimer timer;
|
||||
typedef std::set<LLPointer<LLViewerInventoryCategory>, InventoryIDPtrLess> cat_set_t;
|
||||
cat_set_t temp_cats;
|
||||
bool rv = true;
|
||||
|
|
@ -3204,7 +3205,8 @@ bool LLInventoryModel::loadSkeleton(
|
|||
}
|
||||
|
||||
LL_INFOS(LOG_INV) << "Successfully loaded " << cached_category_count
|
||||
<< " categories and " << cached_item_count << " items from cache."
|
||||
<< " categories and " << cached_item_count << " items from cache"
|
||||
<< " after " << timer.getElapsedTimeF32() << " seconds."
|
||||
<< LL_ENDL;
|
||||
|
||||
return rv;
|
||||
|
|
|
|||
|
|
@ -401,9 +401,28 @@ void LLInventoryPanel::initializeViewBuilding()
|
|||
if (mInventory->isInventoryUsable()
|
||||
&& LLStartUp::getStartupState() <= STATE_WEARABLES_WAIT)
|
||||
{
|
||||
LLTimer timer;
|
||||
// Usually this happens on login, so we have less time constraits, but too long and we can cause a disconnect
|
||||
const F64 max_time = 20.f;
|
||||
initializeViews(max_time);
|
||||
|
||||
if (mViewsInitialized == VIEWS_INITIALIZED)
|
||||
{
|
||||
LL_INFOS("Inventory")
|
||||
<< "Fully initialized inventory panel " << getName()
|
||||
<< " with " << (S32)mItemMap.size()
|
||||
<< " views in " << timer.getElapsedTimeF32() << " seconds."
|
||||
<< LL_ENDL;
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_INFOS("Inventory")
|
||||
<< "Partially initialized inventory panel " << getName()
|
||||
<< " with " << (S32)mItemMap.size()
|
||||
<< " views in " << timer.getElapsedTimeF32()
|
||||
<< " seconds. Pending known views: " << (S32)mBuildViewsQueue.size()
|
||||
<< LL_ENDL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -137,7 +137,8 @@ LLFloaterComboOptions* LLFloaterComboOptions::showUI(
|
|||
{
|
||||
combo_picker->mComboOptions->addSimpleElement(*iter);
|
||||
}
|
||||
combo_picker->mComboOptions->selectFirstItem();
|
||||
// select 'Bulk Upload All' option
|
||||
combo_picker->mComboOptions->selectNthItem((S32)options.size() - 1);
|
||||
|
||||
combo_picker->openFloater(LLSD(title));
|
||||
combo_picker->setFocus(true);
|
||||
|
|
@ -1342,15 +1343,6 @@ const std::string LLMaterialEditor::buildMaterialDescription()
|
|||
desc << mNormalName;
|
||||
}
|
||||
|
||||
// trim last char if it's a ',' in case there is no normal texture
|
||||
// present and the code above inserts one
|
||||
// (no need to check for string length - always has initial string)
|
||||
std::string::iterator iter = desc.str().end() - 1;
|
||||
if (*iter == ',')
|
||||
{
|
||||
desc.str().erase(iter);
|
||||
}
|
||||
|
||||
// sanitize the material description so that it's compatible with the inventory
|
||||
// note: split this up because clang doesn't like operating directly on the
|
||||
// str() - error: lvalue reference to type 'basic_string<...>' cannot bind to a
|
||||
|
|
@ -1358,6 +1350,15 @@ const std::string LLMaterialEditor::buildMaterialDescription()
|
|||
std::string inv_desc = desc.str();
|
||||
LLInventoryObject::correctInventoryName(inv_desc);
|
||||
|
||||
// trim last char if it's a ',' in case there is no normal texture
|
||||
// present and the code above inserts one
|
||||
// (no need to check for string length - always has initial string)
|
||||
std::string::iterator iter = inv_desc.end() - 1;
|
||||
if (*iter == ',')
|
||||
{
|
||||
inv_desc.erase(iter);
|
||||
}
|
||||
|
||||
return inv_desc;
|
||||
}
|
||||
|
||||
|
|
@ -2488,6 +2489,42 @@ void LLMaterialEditor::loadMaterial(const tinygltf::Model &model_in, const std::
|
|||
pack_textures(base_color_img, normal_img, mr_img, emissive_img, occlusion_img,
|
||||
mBaseColorJ2C, mNormalJ2C, mMetallicRoughnessJ2C, mEmissiveJ2C);
|
||||
|
||||
if (open_floater)
|
||||
{
|
||||
bool textures_scaled = false;
|
||||
if (mBaseColorFetched && mBaseColorJ2C
|
||||
&& (mBaseColorFetched->getWidth() != mBaseColorJ2C->getWidth()
|
||||
|| mBaseColorFetched->getHeight() != mBaseColorJ2C->getHeight()))
|
||||
{
|
||||
textures_scaled = true;
|
||||
}
|
||||
else if (mNormalFetched && mNormalJ2C
|
||||
&& (mNormalFetched->getWidth() != mNormalJ2C->getWidth()
|
||||
|| mNormalFetched->getHeight() != mNormalJ2C->getHeight()))
|
||||
{
|
||||
textures_scaled = true;
|
||||
}
|
||||
else if (mMetallicRoughnessFetched && mMetallicRoughnessJ2C
|
||||
&& (mMetallicRoughnessFetched->getWidth() != mMetallicRoughnessJ2C->getWidth()
|
||||
|| mMetallicRoughnessFetched->getHeight() != mMetallicRoughnessJ2C->getHeight()))
|
||||
{
|
||||
textures_scaled = true;
|
||||
}
|
||||
else if (mEmissiveFetched && mEmissiveJ2C
|
||||
&& (mEmissiveFetched->getWidth() != mEmissiveJ2C->getWidth()
|
||||
|| mEmissiveFetched->getHeight() != mEmissiveJ2C->getHeight()))
|
||||
{
|
||||
textures_scaled = true;
|
||||
}
|
||||
|
||||
if (textures_scaled)
|
||||
{
|
||||
LLSD args;
|
||||
args["MAX_SIZE"] = LLViewerTexture::MAX_IMAGE_SIZE_DEFAULT;
|
||||
LLNotificationsUtil::add("MaterialImagesWereScaled", args);
|
||||
}
|
||||
}
|
||||
|
||||
LLUUID base_color_id;
|
||||
if (mBaseColorFetched.notNull())
|
||||
{
|
||||
|
|
@ -2694,10 +2731,8 @@ const std::string LLMaterialEditor::getImageNameFromUri(std::string image_uri, c
|
|||
// so we can include everything
|
||||
if (stripped_uri.length() > 0)
|
||||
{
|
||||
// example "DamagedHelmet: base layer"
|
||||
// example "base layer"
|
||||
return STRINGIZE(
|
||||
mMaterialNameShort <<
|
||||
": " <<
|
||||
stripped_uri <<
|
||||
" (" <<
|
||||
texture_type <<
|
||||
|
|
@ -2706,28 +2741,17 @@ const std::string LLMaterialEditor::getImageNameFromUri(std::string image_uri, c
|
|||
}
|
||||
else
|
||||
// uri doesn't include the type (because the uri is empty)
|
||||
// so we must reorganize the string a bit to include the name
|
||||
// and an explicit name type
|
||||
// include an explicit name type
|
||||
{
|
||||
// example "DamagedHelmet: (Emissive)"
|
||||
return STRINGIZE(
|
||||
mMaterialNameShort <<
|
||||
" (" <<
|
||||
texture_type <<
|
||||
")"
|
||||
);
|
||||
// example "Emissive"
|
||||
return texture_type;
|
||||
}
|
||||
}
|
||||
else
|
||||
// uri includes the type so just use it directly with the
|
||||
// name of the material
|
||||
// uri includes the type so just use it directly
|
||||
{
|
||||
return STRINGIZE(
|
||||
// example: AlienBust: normal_layer
|
||||
mMaterialNameShort <<
|
||||
": " <<
|
||||
stripped_uri
|
||||
);
|
||||
// example: "normal_layer"
|
||||
return stripped_uri;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2502,6 +2502,11 @@ EMeshProcessingResult LLMeshRepoThread::lodReceived(const LLVolumeParams& mesh_p
|
|||
// might be good idea to turn mesh into pointer to avoid making a copy
|
||||
mesh.mVolume = NULL;
|
||||
}
|
||||
{
|
||||
// make sure skin info is not removed from list while we are decreasing reference count
|
||||
LLMutexLock lock(mSkinMapMutex);
|
||||
skin_info = nullptr;
|
||||
}
|
||||
return MESH_OK;
|
||||
}
|
||||
}
|
||||
|
|
@ -2801,10 +2806,14 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures)
|
|||
|
||||
S32 instance_num = 0;
|
||||
|
||||
for (instance_map::iterator iter = mInstance.begin(); iter != mInstance.end(); ++iter)
|
||||
// Handle models, ignore submodels for now.
|
||||
// Probably should pre-sort by mSubmodelID instead of running twice.
|
||||
// Note: mInstance should be sorted by model name for the sake of
|
||||
// deterministic order.
|
||||
for (auto& iter : mInstance)
|
||||
{
|
||||
LLMeshUploadData data;
|
||||
data.mBaseModel = iter->first;
|
||||
data.mBaseModel = iter.first;
|
||||
|
||||
if (data.mBaseModel->mSubmodelID)
|
||||
{
|
||||
|
|
@ -2813,7 +2822,7 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures)
|
|||
continue;
|
||||
}
|
||||
|
||||
LLModelInstance& first_instance = *(iter->second.begin());
|
||||
LLModelInstance& first_instance = *(iter.second.begin());
|
||||
for (S32 i = 0; i < 5; i++)
|
||||
{
|
||||
data.mModel[i] = first_instance.mLOD[i];
|
||||
|
|
@ -2847,7 +2856,7 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures)
|
|||
mUploadSkin,
|
||||
mUploadJoints,
|
||||
mLockScaleIfJointPosition,
|
||||
false,
|
||||
LLModel::WRITE_BINARY,
|
||||
false,
|
||||
data.mBaseModel->mSubmodelID);
|
||||
|
||||
|
|
@ -2860,8 +2869,8 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures)
|
|||
}
|
||||
|
||||
// For all instances that use this model
|
||||
for (instance_list::iterator instance_iter = iter->second.begin();
|
||||
instance_iter != iter->second.end();
|
||||
for (instance_list::iterator instance_iter = iter.second.begin();
|
||||
instance_iter != iter.second.end();
|
||||
++instance_iter)
|
||||
{
|
||||
|
||||
|
|
@ -2959,10 +2968,11 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures)
|
|||
}
|
||||
}
|
||||
|
||||
for (instance_map::iterator iter = mInstance.begin(); iter != mInstance.end(); ++iter)
|
||||
// Now handle the submodels.
|
||||
for (auto& iter : mInstance)
|
||||
{
|
||||
LLMeshUploadData data;
|
||||
data.mBaseModel = iter->first;
|
||||
data.mBaseModel = iter.first;
|
||||
|
||||
if (!data.mBaseModel->mSubmodelID)
|
||||
{
|
||||
|
|
@ -2971,7 +2981,7 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures)
|
|||
continue;
|
||||
}
|
||||
|
||||
LLModelInstance& first_instance = *(iter->second.begin());
|
||||
LLModelInstance& first_instance = *(iter.second.begin());
|
||||
for (S32 i = 0; i < 5; i++)
|
||||
{
|
||||
data.mModel[i] = first_instance.mLOD[i];
|
||||
|
|
@ -3005,7 +3015,7 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures)
|
|||
mUploadSkin,
|
||||
mUploadJoints,
|
||||
mLockScaleIfJointPosition,
|
||||
false,
|
||||
LLModel::WRITE_BINARY,
|
||||
false,
|
||||
data.mBaseModel->mSubmodelID);
|
||||
|
||||
|
|
@ -3018,8 +3028,8 @@ void LLMeshUploadThread::wholeModelToLLSD(LLSD& dest, bool include_textures)
|
|||
}
|
||||
|
||||
// For all instances that use this model
|
||||
for (instance_list::iterator instance_iter = iter->second.begin();
|
||||
instance_iter != iter->second.end();
|
||||
for (instance_list::iterator instance_iter = iter.second.begin();
|
||||
instance_iter != iter.second.end();
|
||||
++instance_iter)
|
||||
{
|
||||
|
||||
|
|
|
|||
|
|
@ -702,7 +702,22 @@ public:
|
|||
typedef std::vector<LLModelInstance> instance_list;
|
||||
instance_list mInstanceList;
|
||||
|
||||
typedef std::map<LLPointer<LLModel>, instance_list> instance_map;
|
||||
// Upload should happen in deterministic order, so sort instances by model name.
|
||||
struct LLUploadModelInstanceLess
|
||||
{
|
||||
inline bool operator()(const LLPointer<LLModel>& a, const LLPointer<LLModel>& b) const
|
||||
{
|
||||
if (a.isNull() || b.isNull())
|
||||
{
|
||||
llassert(false); // We are uploading these models, they shouldn't be null.
|
||||
return true;
|
||||
}
|
||||
// Note: probably can sort by mBaseModel->mSubmodelID here as well to avoid
|
||||
// running over the list twice in wholeModelToLLSD.
|
||||
return a->mLabel < b->mLabel;
|
||||
}
|
||||
};
|
||||
typedef std::map<LLPointer<LLModel>, instance_list, LLUploadModelInstanceLess> instance_map;
|
||||
instance_map mInstance;
|
||||
|
||||
LLMutex* mMutex;
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
|
||||
#include "llmodelloader.h"
|
||||
#include "lldaeloader.h"
|
||||
#include "llgltfloader.h"
|
||||
#include "gltf/llgltfloader.h"
|
||||
#include "llfloatermodelpreview.h"
|
||||
|
||||
#include "llagent.h"
|
||||
|
|
@ -40,6 +40,7 @@
|
|||
#include "lldrawable.h"
|
||||
#include "llface.h"
|
||||
#include "lliconctrl.h"
|
||||
#include "lljointdata.h"
|
||||
#include "llmatrix4a.h"
|
||||
#include "llmeshrepository.h"
|
||||
#include "llmeshoptimizer.h"
|
||||
|
|
@ -218,10 +219,14 @@ LLModelPreview::LLModelPreview(S32 width, S32 height, LLFloater* fmp)
|
|||
, mPhysicsSearchLOD(LLModel::LOD_PHYSICS)
|
||||
, mResetJoints(false)
|
||||
, mModelNoErrors(true)
|
||||
, mLoading(false)
|
||||
, mModelLoader(nullptr)
|
||||
, mLastJointUpdate(false)
|
||||
, mFirstSkinUpdate(true)
|
||||
, mHasDegenerate(false)
|
||||
, mImporterDebug(LLCachedControl<bool>(gSavedSettings, "ImporterDebug", false))
|
||||
, mNumOfFetchingTextures(0)
|
||||
, mTexturesNeedScaling(false)
|
||||
, mImporterDebug(LLCachedControl<bool>(gSavedSettings, "ImporterDebugVerboseLogging", false))
|
||||
{
|
||||
mNeedsUpdate = true;
|
||||
mCameraDistance = 0.f;
|
||||
|
|
@ -230,11 +235,9 @@ LLModelPreview::LLModelPreview(S32 width, S32 height, LLFloater* fmp)
|
|||
mCameraZoom = 1.f;
|
||||
mTextureName = 0;
|
||||
mPreviewLOD = 0;
|
||||
mModelLoader = NULL;
|
||||
mMaxTriangleLimit = 0;
|
||||
mDirty = false;
|
||||
mGenLOD = false;
|
||||
mLoading = false;
|
||||
mLookUpLodFiles = false;
|
||||
mLoadState = LLModelLoader::STARTING;
|
||||
mGroup = 0;
|
||||
|
|
@ -273,6 +276,7 @@ LLModelPreview::~LLModelPreview()
|
|||
{
|
||||
mModelLoader->shutdown();
|
||||
mModelLoader = NULL;
|
||||
mLoading = false;
|
||||
}
|
||||
|
||||
if (mPreviewAvatar)
|
||||
|
|
@ -778,10 +782,7 @@ void LLModelPreview::rebuildUploadData()
|
|||
texture->setLoadedCallback(LLModelPreview::textureLoadedCallback, 0, true, false, new LLHandle<LLModelPreview>(getHandle()), &mCallbackTextureList, false);
|
||||
texture->forceToSaveRawImage(0, F32_MAX);
|
||||
texture->updateFetch();
|
||||
if (mModelLoader)
|
||||
{
|
||||
mModelLoader->mNumOfFetchingTextures++;
|
||||
}
|
||||
mNumOfFetchingTextures++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -925,7 +926,7 @@ void LLModelPreview::saveUploadData(const std::string& filename,
|
|||
save_skinweights,
|
||||
save_joint_positions,
|
||||
lock_scale_if_joint_position,
|
||||
false, true, instance.mModel->mSubmodelID);
|
||||
LLModel::WRITE_BINARY, true, instance.mModel->mSubmodelID);
|
||||
|
||||
data["mesh"][instance.mModel->mLocalID] = str.str();
|
||||
}
|
||||
|
|
@ -987,6 +988,10 @@ void LLModelPreview::loadModel(std::string filename, S32 lod, bool force_disable
|
|||
LL_WARNS() << out.str() << LL_ENDL;
|
||||
LLFloaterModelPreview::addStringToLog(out, true);
|
||||
assert(lod >= LLModel::LOD_IMPOSTOR && lod < LLModel::NUM_LODS);
|
||||
if (mModelLoader == nullptr)
|
||||
{
|
||||
mLoading = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1053,6 +1058,7 @@ void LLModelPreview::loadModel(std::string filename, S32 lod, bool force_disable
|
|||
joint_alias_map,
|
||||
LLSkinningUtil::getMaxJointCount(),
|
||||
gSavedSettings.getU32("ImporterModelLimit"),
|
||||
gSavedSettings.getU32("ImporterDebugMode"),
|
||||
// <FS:Beq> allow LOD suffix configuration
|
||||
//gSavedSettings.getBOOL("ImporterPreprocessDAE"));
|
||||
gSavedSettings.getBOOL("ImporterPreprocessDAE"),
|
||||
|
|
@ -1060,6 +1066,9 @@ void LLModelPreview::loadModel(std::string filename, S32 lod, bool force_disable
|
|||
}
|
||||
else
|
||||
{
|
||||
LLVOAvatar* av = getPreviewAvatar();
|
||||
std::vector<LLJointData> viewer_skeleton;
|
||||
av->getJointMatricesAndHierarhy(viewer_skeleton);
|
||||
mModelLoader = new LLGLTFLoader(
|
||||
filename,
|
||||
lod,
|
||||
|
|
@ -1072,7 +1081,9 @@ void LLModelPreview::loadModel(std::string filename, S32 lod, bool force_disable
|
|||
mJointsFromNode,
|
||||
joint_alias_map,
|
||||
LLSkinningUtil::getMaxJointCount(),
|
||||
gSavedSettings.getU32("ImporterModelLimit"));
|
||||
gSavedSettings.getU32("ImporterModelLimit"),
|
||||
gSavedSettings.getU32("ImporterDebugMode"),
|
||||
viewer_skeleton);
|
||||
}
|
||||
|
||||
if (force_disable_slm)
|
||||
|
|
@ -1286,7 +1297,9 @@ void LLModelPreview::loadModelCallback(S32 loaded_lod)
|
|||
setRigValidForJointPositionUpload(mModelLoader->isRigValidForJointPositionUpload());
|
||||
setLegacyRigFlags(mModelLoader->getLegacyRigFlags());
|
||||
|
||||
mTexturesNeedScaling |= mModelLoader->mTexturesNeedScaling;
|
||||
mModelLoader->loadTextures();
|
||||
warnTextureScaling();
|
||||
|
||||
if (loaded_lod == -1)
|
||||
{ //populate all LoDs from model loader scene
|
||||
|
|
@ -1915,8 +1928,6 @@ void LLModelPreview::genGlodLODs(S32 which_lod, U32 decimation, bool enforce_tri
|
|||
mModel[lod].resize(mBaseModel.size());
|
||||
mVertexBuffer[lod].clear();
|
||||
|
||||
U32 submeshes = 0;
|
||||
|
||||
mRequestedTriangleCount[lod] = (S32)((F32)triangle_count / triangle_ratio);
|
||||
mRequestedErrorThreshold[lod] = lod_error_threshold;
|
||||
|
||||
|
|
@ -2060,7 +2071,6 @@ void LLModelPreview::genGlodLODs(S32 which_lod, U32 decimation, bool enforce_tri
|
|||
buff->getIndexStrider(index);
|
||||
|
||||
target_model->setVolumeFaceData(names[i], pos, norm, tc, index, buff->getNumVerts(), buff->getNumIndices());
|
||||
++submeshes;
|
||||
|
||||
if (!validate_face(target_model->getVolumeFace(names[i])))
|
||||
{
|
||||
|
|
@ -2619,7 +2629,7 @@ F32 LLModelPreview::genMeshOptimizerPerFace(LLModel *base_model, LLModel *target
|
|||
void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 decimation, bool enforce_tri_limit)
|
||||
{
|
||||
// <FS:Beq> Log things properly
|
||||
// LL_INFOS() << "Generating lod " << which_lod << " using meshoptimizer" << LL_ENDL;
|
||||
// LL_DEBUGS("Upload") << "Generating lod " << which_lod << " using meshoptimizer" << LL_ENDL;
|
||||
{
|
||||
std::ostringstream out;
|
||||
out << "Generating lod " << which_lod << " using meshoptimizer";
|
||||
|
|
@ -2703,6 +2713,12 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d
|
|||
|
||||
mMaxTriangleLimit = base_triangle_count;
|
||||
|
||||
// For logging purposes
|
||||
S32 meshes_processed = 0;
|
||||
S32 meshes_simplified = 0;
|
||||
S32 meshes_sloppy_simplified = 0;
|
||||
S32 meshes_fail_count = 0;
|
||||
|
||||
// Build models
|
||||
|
||||
S32 start = LLModel::LOD_HIGH;
|
||||
|
|
@ -2712,7 +2728,7 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d
|
|||
{
|
||||
start = which_lod;
|
||||
end = which_lod;
|
||||
}
|
||||
};
|
||||
|
||||
for (S32 lod = start; lod >= end; --lod)
|
||||
{
|
||||
|
|
@ -2783,6 +2799,11 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d
|
|||
const LLVolumeFace &face = base->getVolumeFace(face_idx);
|
||||
LLVolumeFace &new_face = target_model->getVolumeFace(face_idx);
|
||||
new_face = face;
|
||||
meshes_fail_count++;
|
||||
}
|
||||
else
|
||||
{
|
||||
meshes_simplified++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2795,7 +2816,18 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d
|
|||
if (genMeshOptimizerPerFace(base, target_model, face_idx, indices_decimator, lod_error_threshold, MESH_OPTIMIZER_NO_TOPOLOGY) < 0)
|
||||
{
|
||||
// Sloppy failed and returned an invalid model
|
||||
genMeshOptimizerPerFace(base, target_model, face_idx, indices_decimator, lod_error_threshold, MESH_OPTIMIZER_FULL);
|
||||
if (genMeshOptimizerPerFace(base, target_model, face_idx, indices_decimator, lod_error_threshold, MESH_OPTIMIZER_FULL) < 0)
|
||||
{
|
||||
meshes_fail_count++;
|
||||
}
|
||||
else
|
||||
{
|
||||
meshes_simplified++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
meshes_sloppy_simplified++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2899,7 +2931,7 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d
|
|||
precise_ratio = genMeshOptimizerPerModel(base, target_model, indices_decimator, lod_error_threshold, MESH_OPTIMIZER_FULL);
|
||||
}
|
||||
// <FS:Beq> Log stuff properly
|
||||
// LL_INFOS() << "Model " << target_model->getName()
|
||||
// LL_DEBUGS("Upload") << "Model " << target_model->getName()
|
||||
// << " lod " << which_lod
|
||||
// << " resulting ratio " << precise_ratio
|
||||
// << " simplified using per model method." << LL_ENDL;
|
||||
|
|
@ -2913,11 +2945,12 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d
|
|||
LLFloaterModelPreview::addStringToLog(out, false);
|
||||
}
|
||||
// </FS:Beq>
|
||||
meshes_simplified++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// <FS:Beq> Log stuff properly
|
||||
// LL_INFOS() << "Model " << target_model->getName()
|
||||
// LL_DEBUGS("Upload") << "Model " << target_model->getName()
|
||||
// << " lod " << which_lod
|
||||
// << " resulting ratio " << sloppy_ratio
|
||||
// << " sloppily simplified using per model method." << LL_ENDL;
|
||||
|
|
@ -2929,12 +2962,13 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d
|
|||
LL_INFOS() << out.str() << LL_ENDL;
|
||||
LLFloaterModelPreview::addStringToLog(out, false);
|
||||
// </FS:Beq>
|
||||
meshes_sloppy_simplified++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// <FS:Beq> Log stuff properly
|
||||
// LL_INFOS() << "Model " << target_model->getName()
|
||||
// LL_DEBUGS("Upload") << "Model " << target_model->getName()
|
||||
// << " lod " << which_lod
|
||||
// << " resulting ratio " << precise_ratio
|
||||
// << " simplified using per model method." << LL_ENDL;
|
||||
|
|
@ -2946,6 +2980,7 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d
|
|||
LL_WARNS() << out.str() << LL_ENDL;
|
||||
LLFloaterModelPreview::addStringToLog(out, true);
|
||||
// </FS:Beq>
|
||||
meshes_simplified++;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2959,6 +2994,8 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d
|
|||
//copy material list
|
||||
target_model->mMaterialList = base->mMaterialList;
|
||||
|
||||
meshes_processed++;
|
||||
|
||||
if (!validate_model(target_model))
|
||||
{
|
||||
LL_ERRS() << "Invalid model generated when creating LODs" << LL_ENDL;
|
||||
|
|
@ -2988,6 +3025,11 @@ void LLModelPreview::genMeshOptimizerLODs(S32 which_lod, S32 meshopt_mode, U32 d
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
LL_INFOS("Upload") << "LOD " << which_lod << ", Mesh optimizer processed meshes : " << meshes_processed
|
||||
<<" simplified: " << meshes_simplified
|
||||
<< ", slopily simplified: " << meshes_sloppy_simplified
|
||||
<< ", failures: " << meshes_fail_count << LL_ENDL;
|
||||
}
|
||||
|
||||
void LLModelPreview::updateStatusMessages()
|
||||
|
|
@ -3438,7 +3480,7 @@ void LLModelPreview::updateStatusMessages()
|
|||
LLMutexLock lock(this);
|
||||
if (mModelLoader)
|
||||
{
|
||||
if (!mModelLoader->areTexturesReady() && mFMP->childGetValue("upload_textures").asBoolean())
|
||||
if (!areTexturesReady() && mFMP->childGetValue("upload_textures").asBoolean())
|
||||
{
|
||||
// Some textures are still loading, prevent upload until they are done
|
||||
mModelNoErrors = false;
|
||||
|
|
@ -4073,9 +4115,12 @@ void LLModelPreview::loadedCallback(
|
|||
S32 lod,
|
||||
void* opaque)
|
||||
{
|
||||
if(LLModelPreview::sIgnoreLoadedCallback)
|
||||
return;
|
||||
|
||||
LLModelPreview* pPreview = static_cast<LLModelPreview*>(opaque);
|
||||
LLMutexLock lock(pPreview);
|
||||
if (pPreview && pPreview->mModelLoader && !LLModelPreview::sIgnoreLoadedCallback)
|
||||
if (pPreview && pPreview->mModelLoader)
|
||||
{
|
||||
// Load loader's warnings into floater's log tab
|
||||
const LLSD out = pPreview->mModelLoader->logOut();
|
||||
|
|
@ -4124,44 +4169,48 @@ void LLModelPreview::lookupLODModelFiles(S32 lod)
|
|||
S32 next_lod = (lod - 1 >= LLModel::LOD_IMPOSTOR) ? lod - 1 : LLModel::LOD_PHYSICS;
|
||||
|
||||
std::string lod_filename = mLODFile[LLModel::LOD_HIGH];
|
||||
// <FS:Beq> BUG-230890 fix case-sensitive filename handling
|
||||
// std::string ext = ".dae";
|
||||
// LLStringUtil::toLower(lod_filename_lower);
|
||||
// std::string::size_type i = lod_filename.rfind(ext);
|
||||
// if (i != std::string::npos)
|
||||
// {
|
||||
// lod_filename.replace(i, lod_filename.size() - ext.size(), getLodSuffix(next_lod) + ext);
|
||||
// }
|
||||
// Note: we cannot use gDirUtilp here because the getExtension forces a tolower which would then break uppercase extensions on Linux/Mac
|
||||
std::size_t offset = lod_filename.find_last_of('.');
|
||||
std::string ext = (offset == std::string::npos || offset == 0) ? "" : lod_filename.substr(offset+1);
|
||||
lod_filename = gDirUtilp->getDirName(lod_filename) + gDirUtilp->getDirDelimiter() + stripSuffix(gDirUtilp->getBaseFileName(lod_filename, true)) + getLodSuffix(next_lod) + "." + ext;
|
||||
std::ostringstream out;
|
||||
out << "Looking for file: " << lod_filename << " for LOD " << next_lod;
|
||||
LL_DEBUGS("MeshUpload") << out.str() << LL_ENDL;
|
||||
if(mImporterDebug)
|
||||
std::string lod_filename_lower(lod_filename);
|
||||
LLStringUtil::toLower(lod_filename_lower);
|
||||
|
||||
// Check for each supported file extension
|
||||
std::vector<std::string> supported_exts = { ".dae", ".gltf", ".glb" };
|
||||
std::string found_ext;
|
||||
std::string::size_type ext_pos = std::string::npos;
|
||||
|
||||
for (const auto& ext : supported_exts)
|
||||
{
|
||||
LLFloaterModelPreview::addStringToLog(out, true);
|
||||
}
|
||||
out.str("");
|
||||
// </FS:Beq>
|
||||
if (gDirUtilp->fileExists(lod_filename))
|
||||
{
|
||||
// <FS:Beq> extra logging is helpful here, so add to log tab
|
||||
out << "Auto Loading LOD" << next_lod << " from " << lod_filename;
|
||||
LL_INFOS() << out.str() << LL_ENDL;
|
||||
LLFloaterModelPreview::addStringToLog(out, true);
|
||||
out.str("");
|
||||
// </FS:Beq>
|
||||
LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance;
|
||||
if (fmp)
|
||||
std::string::size_type i = lod_filename_lower.rfind(ext);
|
||||
if (i != std::string::npos)
|
||||
{
|
||||
fmp->setCtrlLoadFromFile(next_lod);
|
||||
ext_pos = i;
|
||||
found_ext = ext;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ext_pos != std::string::npos)
|
||||
{
|
||||
// Replace extension with LOD suffix + original extension
|
||||
std::string lod_file_to_check = lod_filename;
|
||||
lod_file_to_check.replace(ext_pos, found_ext.size(), getLodSuffix(next_lod) + found_ext);
|
||||
|
||||
if (gDirUtilp->fileExists(lod_file_to_check))
|
||||
{
|
||||
LLFloaterModelPreview* fmp = LLFloaterModelPreview::sInstance;
|
||||
if (fmp)
|
||||
{
|
||||
fmp->setCtrlLoadFromFile(next_lod);
|
||||
}
|
||||
loadModel(lod_file_to_check, next_lod);
|
||||
}
|
||||
else
|
||||
{
|
||||
lookupLODModelFiles(next_lod);
|
||||
}
|
||||
loadModel(lod_filename, next_lod);
|
||||
}
|
||||
else
|
||||
{
|
||||
// No recognized extension found, continue with next LOD
|
||||
lookupLODModelFiles(next_lod);
|
||||
}
|
||||
}
|
||||
|
|
@ -4202,6 +4251,7 @@ U32 LLModelPreview::loadTextures(LLImportMaterial& material, LLHandle<LLModelPre
|
|||
tex->setLoadedCallback(LLModelPreview::textureLoadedCallback, 0, true, false, new LLHandle<LLModelPreview>(handle), &preview->mCallbackTextureList, false);
|
||||
tex->forceToSaveRawImage(0, F32_MAX);
|
||||
material.setDiffuseMap(tex->getID()); // record tex ID
|
||||
preview->mNumOfFetchingTextures++;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
@ -5135,6 +5185,18 @@ void LLModelPreview::setPreviewLOD(S32 lod)
|
|||
updateStatusMessages();
|
||||
}
|
||||
|
||||
void LLModelPreview::warnTextureScaling()
|
||||
{
|
||||
if (areTexturesReady() && mTexturesNeedScaling)
|
||||
{
|
||||
std::ostringstream out;
|
||||
out << "One or more textures in this model were scaled to be within the allowed limits.";
|
||||
LL_INFOS() << out.str() << LL_ENDL;
|
||||
LLSD args;
|
||||
LLFloaterModelPreview::addStringToLog("ModelTextureScaling", args, true, -1);
|
||||
}
|
||||
}
|
||||
|
||||
//static
|
||||
void LLModelPreview::textureLoadedCallback(
|
||||
bool success,
|
||||
|
|
@ -5155,11 +5217,19 @@ void LLModelPreview::textureLoadedCallback(
|
|||
LLModelPreview* preview = static_cast<LLModelPreview*>(handle->get());
|
||||
preview->refresh();
|
||||
|
||||
if (final && preview->mModelLoader)
|
||||
if (final)
|
||||
{
|
||||
if (preview->mModelLoader->mNumOfFetchingTextures > 0)
|
||||
if (src_vi
|
||||
&& (src_vi->getOriginalWidth() > LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT
|
||||
|| src_vi->getOriginalHeight() > LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT))
|
||||
{
|
||||
preview->mModelLoader->mNumOfFetchingTextures--;
|
||||
preview->mTexturesNeedScaling = true;
|
||||
}
|
||||
|
||||
if (preview->mNumOfFetchingTextures > 0)
|
||||
{
|
||||
preview->mNumOfFetchingTextures--;
|
||||
preview->warnTextureScaling();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -210,6 +210,7 @@ public:
|
|||
std::vector<S32> mLodsQuery;
|
||||
std::vector<S32> mLodsWithParsingError;
|
||||
bool mHasDegenerate;
|
||||
bool areTexturesReady() { return !mNumOfFetchingTextures; }
|
||||
|
||||
protected:
|
||||
bool matchMaterialOrder(LLModel* lod, LLModel* ref, int& refFaceCnt, int& modelFaceCnt ); // <FS:Beq/> FIRE-30965 Cleanup mesh material parsing
|
||||
|
|
@ -219,6 +220,7 @@ protected:
|
|||
static LLJoint* lookupJointByName(const std::string&, void* opaque);
|
||||
static U32 loadTextures(LLImportMaterial& material, LLHandle<LLModelPreview> handle);
|
||||
|
||||
void warnTextureScaling();
|
||||
void lookupLODModelFiles(S32 lod);
|
||||
|
||||
private:
|
||||
|
|
@ -248,6 +250,9 @@ private:
|
|||
/// Not read unless mWarnOfUnmatchedPhyicsMeshes is true.
|
||||
LLPointer<LLModel> mDefaultPhysicsShapeP{nullptr};
|
||||
|
||||
S32 mNumOfFetchingTextures;
|
||||
bool mTexturesNeedScaling;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
MESH_OPTIMIZER_FULL,
|
||||
|
|
|
|||
|
|
@ -164,6 +164,36 @@ void LLPanelFace::updateSelectedGLTFMaterials(std::function<void(LLGLTFMaterial*
|
|||
LLSelectMgr::getInstance()->getSelection()->applyToTEs(&select_func);
|
||||
}
|
||||
|
||||
void LLPanelFace::updateSelectedGLTFMaterialsWithScale(std::function<void(LLGLTFMaterial*, const F32, const F32)> func)
|
||||
{
|
||||
struct LLSelectedTEGLTFMaterialFunctor : public LLSelectedTEFunctor
|
||||
{
|
||||
LLSelectedTEGLTFMaterialFunctor(std::function<void(LLGLTFMaterial*, const F32, const F32)> func) : mFunc(func) {}
|
||||
virtual ~LLSelectedTEGLTFMaterialFunctor() {};
|
||||
bool apply(LLViewerObject* object, S32 face) override
|
||||
{
|
||||
LLGLTFMaterial new_override;
|
||||
const LLTextureEntry* tep = object->getTE(face);
|
||||
if (tep->getGLTFMaterialOverride())
|
||||
{
|
||||
new_override = *tep->getGLTFMaterialOverride();
|
||||
}
|
||||
|
||||
U32 s_axis = VX;
|
||||
U32 t_axis = VY;
|
||||
LLPrimitive::getTESTAxes(face, &s_axis, &t_axis);
|
||||
mFunc(&new_override, object->getScale().mV[s_axis], object->getScale().mV[t_axis]);
|
||||
LLGLTFMaterialList::queueModify(object, face, &new_override);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::function<void(LLGLTFMaterial*, const F32, const F32)> mFunc;
|
||||
} select_func(func);
|
||||
|
||||
LLSelectMgr::getInstance()->getSelection()->applyToTEs(&select_func);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void readSelectedGLTFMaterial(std::function<T(const LLGLTFMaterial*)> func, T& value, bool& identical, bool has_tolerance, T tolerance)
|
||||
{
|
||||
|
|
@ -184,6 +214,36 @@ void readSelectedGLTFMaterial(std::function<T(const LLGLTFMaterial*)> func, T& v
|
|||
identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue(&select_func, value, has_tolerance, tolerance);
|
||||
}
|
||||
|
||||
void getSelectedGLTFMaterialMaxRepeats(LLGLTFMaterial::TextureInfo channel, F32& repeats, bool& identical)
|
||||
{
|
||||
// The All channel should read base color values
|
||||
if (channel == LLGLTFMaterial::TextureInfo::GLTF_TEXTURE_INFO_COUNT)
|
||||
channel = LLGLTFMaterial::TextureInfo::GLTF_TEXTURE_INFO_BASE_COLOR;
|
||||
|
||||
struct LLSelectedTEGetGLTFMaterialMaxRepeatsFunctor : public LLSelectedTEGetFunctor<F32>
|
||||
{
|
||||
LLSelectedTEGetGLTFMaterialMaxRepeatsFunctor(LLGLTFMaterial::TextureInfo channel) : mChannel(channel) {}
|
||||
virtual ~LLSelectedTEGetGLTFMaterialMaxRepeatsFunctor() {};
|
||||
F32 get(LLViewerObject* object, S32 face) override
|
||||
{
|
||||
const LLTextureEntry* tep = object->getTE(face);
|
||||
const LLGLTFMaterial* render_material = tep->getGLTFRenderMaterial();
|
||||
if (!render_material)
|
||||
return 0.f;
|
||||
|
||||
U32 s_axis = VX;
|
||||
U32 t_axis = VY;
|
||||
LLPrimitive::getTESTAxes(face, &s_axis, &t_axis);
|
||||
F32 repeats_u = render_material->mTextureTransform[mChannel].mScale[VX] / object->getScale().mV[s_axis];
|
||||
F32 repeats_v = render_material->mTextureTransform[mChannel].mScale[VY] / object->getScale().mV[t_axis];
|
||||
return llmax(repeats_u, repeats_v);
|
||||
}
|
||||
|
||||
LLGLTFMaterial::TextureInfo mChannel;
|
||||
} max_repeats_func(channel);
|
||||
identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue(&max_repeats_func, repeats);
|
||||
}
|
||||
|
||||
BOOST_STATIC_ASSERT(MATTYPE_DIFFUSE == LLRender::DIFFUSE_MAP && MATTYPE_NORMAL == LLRender::NORMAL_MAP && MATTYPE_SPECULAR == LLRender::SPECULAR_MAP);
|
||||
|
||||
//
|
||||
|
|
@ -332,6 +392,7 @@ bool LLPanelFace::postBuild()
|
|||
|
||||
getChildSetCommitCallback(mPBRScaleU, "gltfTextureScaleU", [&](LLUICtrl*, const LLSD&) { onCommitGLTFTextureScaleU(); });
|
||||
getChildSetCommitCallback(mPBRScaleV, "gltfTextureScaleV", [&](LLUICtrl*, const LLSD&) { onCommitGLTFTextureScaleV(); });
|
||||
getChildSetCommitCallback(mPBRRepeat, "gltfRptctrl", [&](LLUICtrl*, const LLSD&) { onCommitGLTFRepeatsPerMeter(); });
|
||||
getChildSetCommitCallback(mPBRRotate, "gltfTextureRotation", [&](LLUICtrl*, const LLSD&) { onCommitGLTFRotation(); });
|
||||
getChildSetCommitCallback(mPBROffsetU, "gltfTextureOffsetU", [&](LLUICtrl*, const LLSD&) { onCommitGLTFTextureOffsetU(); });
|
||||
getChildSetCommitCallback(mPBROffsetV, "gltfTextureOffsetV", [&](LLUICtrl*, const LLSD&) { onCommitGLTFTextureOffsetV(); });
|
||||
|
|
@ -1446,9 +1507,18 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/)
|
|||
spec_scale_s = editable ? spec_scale_s : 1.0f;
|
||||
spec_scale_s *= identical_planar_texgen ? 2.0f : 1.0f;
|
||||
|
||||
mTexScaleU->setValue(diff_scale_s);
|
||||
mShinyScaleU->setValue(spec_scale_s);
|
||||
mBumpyScaleU->setValue(norm_scale_s);
|
||||
if (force_set_values)
|
||||
{
|
||||
mTexScaleU->forceSetValue(diff_scale_s);
|
||||
mShinyScaleU->forceSetValue(spec_scale_s);
|
||||
mBumpyScaleU->forceSetValue(norm_scale_s);
|
||||
}
|
||||
else
|
||||
{
|
||||
mTexScaleU->setValue(diff_scale_s);
|
||||
mShinyScaleU->setValue(spec_scale_s);
|
||||
mBumpyScaleU->setValue(norm_scale_s);
|
||||
}
|
||||
|
||||
mTexScaleU->setEnabled(editable && has_material);
|
||||
// <FS:CR> Materials alignment
|
||||
|
|
@ -1505,13 +1575,16 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/)
|
|||
if (force_set_values)
|
||||
{
|
||||
mTexScaleV->forceSetValue(diff_scale_t);
|
||||
mShinyScaleV->forceSetValue(spec_scale_t);
|
||||
mBumpyScaleV->forceSetValue(norm_scale_t);
|
||||
}
|
||||
else
|
||||
{
|
||||
mTexScaleV->setValue(diff_scale_t);
|
||||
mShinyScaleV->setValue(spec_scale_t);
|
||||
mBumpyScaleV->setValue(norm_scale_t);
|
||||
}
|
||||
mShinyScaleV->setValue(spec_scale_t);
|
||||
mBumpyScaleV->setValue(norm_scale_t);
|
||||
|
||||
|
||||
mTexScaleV->setTentative(LLSD(diff_scale_tentative));
|
||||
mShinyScaleV->setTentative(LLSD(spec_scale_tentative));
|
||||
|
|
@ -1660,36 +1733,57 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/)
|
|||
F32 repeats_norm = 1.f;
|
||||
F32 repeats_spec = 1.f;
|
||||
|
||||
F32 repeats_pbr_basecolor = 1.f;
|
||||
F32 repeats_pbr_metallic_roughness = 1.f;
|
||||
F32 repeats_pbr_normal = 1.f;
|
||||
F32 repeats_pbr_emissive = 1.f;
|
||||
|
||||
bool identical_diff_repeats = false;
|
||||
bool identical_norm_repeats = false;
|
||||
bool identical_spec_repeats = false;
|
||||
|
||||
LLSelectedTE::getMaxDiffuseRepeats(repeats_diff, identical_diff_repeats);
|
||||
LLSelectedTEMaterial::getMaxNormalRepeats(repeats_norm, identical_norm_repeats);
|
||||
LLSelectedTEMaterial::getMaxSpecularRepeats(repeats_spec, identical_spec_repeats);
|
||||
bool identical_pbr_basecolor_repeats = false;
|
||||
bool identical_pbr_metallic_roughness_repeats = false;
|
||||
bool identical_pbr_normal_repeats = false;
|
||||
bool identical_pbr_emissive_repeats = false;
|
||||
|
||||
{
|
||||
LLSpinCtrl* repeats_spin_ctrl = nullptr;
|
||||
S32 index = mComboTexGen ? mComboTexGen->getCurrentIndex() : 0;
|
||||
bool enabled = editable && (index != 1);
|
||||
bool identical_repeats = true;
|
||||
S32 material_selection = mComboMatMedia->getCurrentIndex();
|
||||
F32 repeats = 1.0f;
|
||||
|
||||
U32 material_type = MATTYPE_DIFFUSE;
|
||||
if (material_selection == MATMEDIA_MATERIAL)
|
||||
LLRender::eTexIndex material_channel = LLRender::DIFFUSE_MAP;
|
||||
if (material_selection != MATMEDIA_PBR)
|
||||
{
|
||||
material_type = mRadioMaterialType->getSelectedIndex();
|
||||
repeats_spin_ctrl = mTexRepeat;
|
||||
material_channel = getMatTextureChannel();
|
||||
LLSelectedTE::getMaxDiffuseRepeats(repeats_diff, identical_diff_repeats);
|
||||
LLSelectedTEMaterial::getMaxNormalRepeats(repeats_norm, identical_norm_repeats);
|
||||
LLSelectedTEMaterial::getMaxSpecularRepeats(repeats_spec, identical_spec_repeats);
|
||||
}
|
||||
else if (material_selection == MATMEDIA_PBR)
|
||||
{
|
||||
repeats_spin_ctrl = mPBRRepeat;
|
||||
enabled = editable && has_pbr_material;
|
||||
material_type = mRadioPbrType->getSelectedIndex();
|
||||
material_channel = getPBRTextureChannel();
|
||||
|
||||
getSelectedGLTFMaterialMaxRepeats(LLGLTFMaterial::TextureInfo::GLTF_TEXTURE_INFO_BASE_COLOR,
|
||||
repeats_pbr_basecolor, identical_pbr_basecolor_repeats);
|
||||
getSelectedGLTFMaterialMaxRepeats(LLGLTFMaterial::TextureInfo::GLTF_TEXTURE_INFO_METALLIC_ROUGHNESS,
|
||||
repeats_pbr_metallic_roughness, identical_pbr_metallic_roughness_repeats);
|
||||
getSelectedGLTFMaterialMaxRepeats(LLGLTFMaterial::TextureInfo::GLTF_TEXTURE_INFO_NORMAL,
|
||||
repeats_pbr_normal, identical_pbr_normal_repeats);
|
||||
getSelectedGLTFMaterialMaxRepeats(LLGLTFMaterial::TextureInfo::GLTF_TEXTURE_INFO_EMISSIVE,
|
||||
repeats_pbr_emissive, identical_pbr_emissive_repeats);
|
||||
}
|
||||
|
||||
switch (material_type)
|
||||
switch (material_channel)
|
||||
{
|
||||
default:
|
||||
case MATTYPE_DIFFUSE:
|
||||
case LLRender::DIFFUSE_MAP:
|
||||
if (material_selection != MATMEDIA_PBR)
|
||||
{
|
||||
enabled = editable && !id.isNull();
|
||||
|
|
@ -1697,7 +1791,7 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/)
|
|||
identical_repeats = identical_diff_repeats;
|
||||
repeats = repeats_diff;
|
||||
break;
|
||||
case MATTYPE_SPECULAR:
|
||||
case LLRender::SPECULAR_MAP:
|
||||
if (material_selection != MATMEDIA_PBR)
|
||||
{
|
||||
enabled = (editable && ((shiny == SHINY_TEXTURE) && !specmap_id.isNull())
|
||||
|
|
@ -1706,7 +1800,7 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/)
|
|||
identical_repeats = identical_spec_repeats;
|
||||
repeats = repeats_spec;
|
||||
break;
|
||||
case MATTYPE_NORMAL:
|
||||
case LLRender::NORMAL_MAP:
|
||||
if (material_selection != MATMEDIA_PBR)
|
||||
{
|
||||
enabled = (editable && ((bumpy == BUMPY_TEXTURE) && !normmap_id.isNull())
|
||||
|
|
@ -1715,6 +1809,23 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/)
|
|||
identical_repeats = identical_norm_repeats;
|
||||
repeats = repeats_norm;
|
||||
break;
|
||||
case LLRender::NUM_TEXTURE_CHANNELS:
|
||||
case LLRender::BASECOLOR_MAP:
|
||||
identical_repeats = identical_pbr_basecolor_repeats;
|
||||
repeats = repeats_pbr_basecolor;
|
||||
break;
|
||||
case LLRender::METALLIC_ROUGHNESS_MAP:
|
||||
identical_repeats = identical_pbr_metallic_roughness_repeats;
|
||||
repeats = repeats_pbr_metallic_roughness;
|
||||
break;
|
||||
case LLRender::GLTF_NORMAL_MAP:
|
||||
identical_repeats = identical_pbr_normal_repeats;
|
||||
repeats = repeats_pbr_normal;
|
||||
break;
|
||||
case LLRender::EMISSIVE_MAP:
|
||||
identical_repeats = identical_pbr_emissive_repeats;
|
||||
repeats = repeats_pbr_emissive;
|
||||
break;
|
||||
}
|
||||
|
||||
bool repeats_tentative = !identical_repeats;
|
||||
|
|
@ -1722,14 +1833,14 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/)
|
|||
if (force_set_values)
|
||||
{
|
||||
// onCommit, previosly edited element updates related ones
|
||||
mTexRepeat->forceSetValue(editable ? repeats : 1.0f);
|
||||
repeats_spin_ctrl->forceSetValue(editable ? repeats : 1.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
mTexRepeat->setValue(editable ? repeats : 1.0f);
|
||||
repeats_spin_ctrl->setValue(editable ? repeats : 1.0f);
|
||||
}
|
||||
mTexRepeat->setTentative(LLSD(repeats_tentative));
|
||||
mTexRepeat->setEnabled(has_material && !identical_planar_texgen && enabled);
|
||||
repeats_spin_ctrl->setTentative(LLSD(repeats_tentative));
|
||||
repeats_spin_ctrl->setEnabled(!identical_planar_texgen && enabled);
|
||||
// <FS:CR> FIRE-11407 - Flip buttons
|
||||
mBtnTexFlipScaleU->setEnabled(enabled);
|
||||
mBtnTexFlipScaleV->setEnabled(enabled);
|
||||
|
|
@ -1889,6 +2000,7 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/)
|
|||
}
|
||||
mLabelColorTransp->setEnabled(false);
|
||||
mTexRepeat->setEnabled(false);
|
||||
mPBRRepeat->setEnabled(false);
|
||||
mLabelTexGen->setEnabled(false);
|
||||
mLabelShininess->setEnabled(false);
|
||||
mLabelBumpiness->setEnabled(false);
|
||||
|
|
@ -2089,6 +2201,7 @@ void LLPanelFace::updateVisibilityGLTF(LLViewerObject* objectp /*= nullptr */)
|
|||
mPBRRotate->setVisible(show_pbr);
|
||||
mPBROffsetU->setVisible(show_pbr);
|
||||
mPBROffsetV->setVisible(show_pbr);
|
||||
mPBRRepeat->setVisible(show_pbr);
|
||||
}
|
||||
|
||||
void LLPanelFace::updateCopyTexButton()
|
||||
|
|
@ -3760,18 +3873,8 @@ void LLPanelFace::onCommitRepeatsPerMeter()
|
|||
if (gSavedSettings.getBOOL("SyncMaterialSettings"))
|
||||
{
|
||||
LLSelectMgr::getInstance()->selectionTexScaleAutofit(repeats_per_meter);
|
||||
|
||||
mBumpyScaleU->setValue(obj_scale_s * repeats_per_meter);
|
||||
mBumpyScaleV->setValue(obj_scale_t * repeats_per_meter);
|
||||
|
||||
LLSelectedTEMaterial::setNormalRepeatX(this, obj_scale_s * repeats_per_meter);
|
||||
LLSelectedTEMaterial::setNormalRepeatY(this, obj_scale_t * repeats_per_meter);
|
||||
|
||||
mShinyScaleU->setValue(obj_scale_s * repeats_per_meter);
|
||||
mShinyScaleV->setValue(obj_scale_t * repeats_per_meter);
|
||||
|
||||
LLSelectedTEMaterial::setSpecularRepeatX(this, obj_scale_s * repeats_per_meter);
|
||||
LLSelectedTEMaterial::setSpecularRepeatY(this, obj_scale_t * repeats_per_meter);
|
||||
LLSelectedTEMaterial::selectionNormalScaleAutofit(this, repeats_per_meter);
|
||||
LLSelectedTEMaterial::selectionSpecularScaleAutofit(this, repeats_per_meter);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -3782,18 +3885,10 @@ void LLPanelFace::onCommitRepeatsPerMeter()
|
|||
LLSelectMgr::getInstance()->selectionTexScaleAutofit(repeats_per_meter);
|
||||
break;
|
||||
case MATTYPE_NORMAL:
|
||||
mBumpyScaleU->setValue(obj_scale_s * repeats_per_meter);
|
||||
mBumpyScaleV->setValue(obj_scale_t * repeats_per_meter);
|
||||
|
||||
LLSelectedTEMaterial::setNormalRepeatX(this, obj_scale_s * repeats_per_meter);
|
||||
LLSelectedTEMaterial::setNormalRepeatY(this, obj_scale_t * repeats_per_meter);
|
||||
LLSelectedTEMaterial::selectionNormalScaleAutofit(this, repeats_per_meter);
|
||||
break;
|
||||
case MATTYPE_SPECULAR:
|
||||
mBumpyScaleU->setValue(obj_scale_s * repeats_per_meter);
|
||||
mBumpyScaleV->setValue(obj_scale_t * repeats_per_meter);
|
||||
|
||||
LLSelectedTEMaterial::setSpecularRepeatX(this, obj_scale_s * repeats_per_meter);
|
||||
LLSelectedTEMaterial::setSpecularRepeatY(this, obj_scale_t * repeats_per_meter);
|
||||
LLSelectedTEMaterial::selectionSpecularScaleAutofit(this, repeats_per_meter);
|
||||
break;
|
||||
default:
|
||||
llassert(false);
|
||||
|
|
@ -3804,6 +3899,21 @@ void LLPanelFace::onCommitRepeatsPerMeter()
|
|||
updateUI(true);
|
||||
}
|
||||
|
||||
// Commit the number of GLTF repeats per meter
|
||||
void LLPanelFace::onCommitGLTFRepeatsPerMeter()
|
||||
{
|
||||
F32 repeats_per_meter = (F32)mPBRRepeat->getValue().asReal();
|
||||
|
||||
LLGLTFMaterial::TextureInfo material_type = getPBRTextureInfo();
|
||||
updateGLTFTextureTransformWithScale(material_type, [&](LLGLTFMaterial::TextureTransform* new_transform, F32 scale_s, F32 scale_t)
|
||||
{
|
||||
new_transform->mScale.mV[VX] = scale_s * repeats_per_meter;
|
||||
new_transform->mScale.mV[VY] = scale_t * repeats_per_meter;
|
||||
});
|
||||
|
||||
updateUI(true);
|
||||
}
|
||||
|
||||
struct LLPanelFaceSetMediaFunctor : public LLSelectedTEFunctor
|
||||
{
|
||||
virtual bool apply(LLViewerObject* object, S32 te)
|
||||
|
|
@ -4928,6 +5038,29 @@ void LLPanelFace::updateGLTFTextureTransform(std::function<void(LLGLTFMaterial::
|
|||
}
|
||||
}
|
||||
|
||||
void LLPanelFace::updateGLTFTextureTransformWithScale(const LLGLTFMaterial::TextureInfo texture_info, std::function<void(LLGLTFMaterial::TextureTransform*, const F32, const F32)> edit)
|
||||
{
|
||||
if (texture_info == LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT)
|
||||
{
|
||||
updateSelectedGLTFMaterialsWithScale([&](LLGLTFMaterial* new_override, const F32 scale_s, const F32 scale_t)
|
||||
{
|
||||
for (U32 i = 0; i < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT; ++i)
|
||||
{
|
||||
LLGLTFMaterial::TextureTransform& new_transform = new_override->mTextureTransform[(LLGLTFMaterial::TextureInfo)i];
|
||||
edit(&new_transform, scale_s, scale_t);
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
updateSelectedGLTFMaterialsWithScale([&](LLGLTFMaterial* new_override, const F32 scale_s, const F32 scale_t)
|
||||
{
|
||||
LLGLTFMaterial::TextureTransform& new_transform = new_override->mTextureTransform[texture_info];
|
||||
edit(&new_transform, scale_s, scale_t);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void LLPanelFace::setMaterialOverridesFromSelection()
|
||||
{
|
||||
const LLGLTFMaterial::TextureInfo texture_info = getPBRTextureInfo();
|
||||
|
|
@ -5003,8 +5136,9 @@ void LLPanelFace::setMaterialOverridesFromSelection()
|
|||
}
|
||||
}
|
||||
|
||||
mPBRScaleU->setValue(transform.mScale[VX]);
|
||||
mPBRScaleV->setValue(transform.mScale[VY]);
|
||||
// Force set scales just in case they were set by repeats per meter and their spinner is focused
|
||||
mPBRScaleU->forceSetValue(transform.mScale[VX]);
|
||||
mPBRScaleV->forceSetValue(transform.mScale[VY]);
|
||||
mPBRRotate->setValue(transform.mRotation * RAD_TO_DEG);
|
||||
mPBROffsetU->setValue(transform.mOffset[VX]);
|
||||
mPBROffsetV->setValue(transform.mOffset[VY]);
|
||||
|
|
@ -5014,6 +5148,12 @@ void LLPanelFace::setMaterialOverridesFromSelection()
|
|||
mPBRRotate->setTentative(!rotation_same);
|
||||
mPBROffsetU->setTentative(!offset_u_same);
|
||||
mPBROffsetV->setTentative(!offset_v_same);
|
||||
|
||||
F32 repeats = 1.f;
|
||||
bool identical = false;
|
||||
getSelectedGLTFMaterialMaxRepeats(getPBRDropChannel(), repeats, identical);
|
||||
mPBRRepeat->forceSetValue(repeats);
|
||||
mPBRRepeat->setTentative(!identical || !scale_u_same || !scale_v_same);
|
||||
}
|
||||
|
||||
void LLPanelFace::Selection::connect()
|
||||
|
|
@ -5507,6 +5647,62 @@ void LLPanelFace::LLSelectedTEMaterial::getCurrentDiffuseAlphaMode(U8& diffuse_a
|
|||
identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &get_diff_mode, diffuse_alpha_mode);
|
||||
}
|
||||
|
||||
void LLPanelFace::LLSelectedTEMaterial::selectionNormalScaleAutofit(LLPanelFace* panel_face, F32 repeats_per_meter)
|
||||
{
|
||||
struct f : public LLSelectedTEFunctor
|
||||
{
|
||||
LLPanelFace* mFacePanel;
|
||||
F32 mRepeatsPerMeter;
|
||||
f(LLPanelFace* face_panel, const F32& repeats_per_meter) : mFacePanel(face_panel), mRepeatsPerMeter(repeats_per_meter) {}
|
||||
bool apply(LLViewerObject* object, S32 te)
|
||||
{
|
||||
if (object->permModify())
|
||||
{
|
||||
// Compute S,T to axis mapping
|
||||
U32 s_axis, t_axis;
|
||||
if (!LLPrimitive::getTESTAxes(te, &s_axis, &t_axis))
|
||||
return true;
|
||||
|
||||
F32 new_s = object->getScale().mV[s_axis] * mRepeatsPerMeter;
|
||||
F32 new_t = object->getScale().mV[t_axis] * mRepeatsPerMeter;
|
||||
|
||||
setNormalRepeatX(mFacePanel, new_s, te);
|
||||
setNormalRepeatY(mFacePanel, new_t, te);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} setfunc(panel_face, repeats_per_meter);
|
||||
LLSelectMgr::getInstance()->getSelection()->applyToTEs(&setfunc);
|
||||
}
|
||||
|
||||
void LLPanelFace::LLSelectedTEMaterial::selectionSpecularScaleAutofit(LLPanelFace* panel_face, F32 repeats_per_meter)
|
||||
{
|
||||
struct f : public LLSelectedTEFunctor
|
||||
{
|
||||
LLPanelFace* mFacePanel;
|
||||
F32 mRepeatsPerMeter;
|
||||
f(LLPanelFace* face_panel, const F32& repeats_per_meter) : mFacePanel(face_panel), mRepeatsPerMeter(repeats_per_meter) {}
|
||||
bool apply(LLViewerObject* object, S32 te)
|
||||
{
|
||||
if (object->permModify())
|
||||
{
|
||||
// Compute S,T to axis mapping
|
||||
U32 s_axis, t_axis;
|
||||
if (!LLPrimitive::getTESTAxes(te, &s_axis, &t_axis))
|
||||
return true;
|
||||
|
||||
F32 new_s = object->getScale().mV[s_axis] * mRepeatsPerMeter;
|
||||
F32 new_t = object->getScale().mV[t_axis] * mRepeatsPerMeter;
|
||||
|
||||
setSpecularRepeatX(mFacePanel, new_s, te);
|
||||
setSpecularRepeatY(mFacePanel, new_t, te);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} setfunc(panel_face, repeats_per_meter);
|
||||
LLSelectMgr::getInstance()->getSelection()->applyToTEs(&setfunc);
|
||||
}
|
||||
|
||||
void LLPanelFace::LLSelectedTE::getObjectScaleS(F32& scale_s, bool& identical)
|
||||
{
|
||||
struct LLSelectedTEGetObjectScaleS : public LLSelectedTEGetFunctor<F32>
|
||||
|
|
@ -5702,6 +5898,7 @@ void LLPanelFace::changePrecision(S32 decimal_precision)
|
|||
mPBRRotate->setPrecision(decimal_precision);
|
||||
mPBROffsetU->setPrecision(decimal_precision);
|
||||
mPBROffsetV->setPrecision(decimal_precision);
|
||||
mPBRRepeat->setPrecision(decimal_precision);
|
||||
}
|
||||
// </FS:CR>
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue