Skip to content

Commit

Permalink
feat: remove animation canvas entirely | move to animation package (#…
Browse files Browse the repository at this point in the history
…1457)

Description
---

- most of the work was done in the new ~~[`tari-tower`
repo](https://github.com/tari-labs/tari-tower)~~
[`@tari-project/tari-tower`
repo](https://github.com/tari-project/tari-tower)
- implemented typescript, and converted Classes to modules where i could
  - fixed type errors
- added load/remove tower functions which are exported along with
`setAnimationState` and `setAnimationProperties`
  - built and published as a lib 
- added
[`@tari-project/tari-tower`](https://www.npmjs.com/package/@tari-project/tari-tower)
package
- removed old webgl build files 
- updated imports 
- added extra toggle loading state flag (to help in
`useMiningUiStateMachine.ts`)
- updated workflows so we can use the package


Motivation and Context
---

- toggling visual mode previously just hid the canvas, this removes the
element entirely + frees up some gpu after removing the WEBGL renderer

How Has This Been Tested?
---


https://github.com/user-attachments/assets/c9b92155-1edb-4070-95c8-6834a863158a



https://github.com/user-attachments/assets/9b9b5c95-73a8-437e-ac1e-052498341f1b


https://github.com/user-attachments/assets/2fd12be6-ac7f-45bd-a48c-253a8036a750



What process can a PR reviewer use to test or verify this change?
---

- run with visual mode on vs off and see GPU stats

Breaking Changes
---

- ~~yes, after this is merged you will need to install
`@tari-labs/tari-tower` for local development~~
- nope, just `npm ci` after it's merged :3


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Introduced an enhanced visual mode with improved loading indicators
and smoother animation transitions powered by an external graphics
module.

- **Refactor**
- Streamlined the page layout by removing embedded graphics
initialization code and redundant elements, resulting in more efficient
loading and clearer error feedback.

- **Style**
- Updated styling for key visual elements with revised identifiers to
ensure a consistent and polished interface.

- **Bug Fixes**
- Improved error handling for WebGL support notifications and visual
mode toggling.

- **Chores**
	- Updated dependency versions for improved performance and stability.
	- Removed outdated licensing documentation for texture assets.
- Removed unused TypeScript declarations and related files to clean up
the codebase.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Brian Pearce <[email protected]>
  • Loading branch information
shanimal08 and brianp authored Feb 28, 2025
1 parent 39ad3bd commit 333aa88
Show file tree
Hide file tree
Showing 28 changed files with 265 additions and 5,057 deletions.
1 change: 0 additions & 1 deletion .license.ignore
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@
./public/assets/glApp.js
71 changes: 3 additions & 68 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@
crossorigin
/>

<script type="module" crossorigin src="/assets/glApp.js"></script>
<title>Tari Universe | Testnet</title>
<style>
/* Poppins Font Faces */
Expand Down Expand Up @@ -190,78 +189,14 @@
padding: 0;
margin: 0;
}

#canvas {
#tower-canvas {
position: absolute;
pointer-events: auto;
}
</style>
</head>
<body>
<main id="main">
<div id="root"></div>
<canvas id="canvas" style="opacity: 0"></canvas>
<script type="module" src="src/main.tsx"></script>
<script>
let time;
let lastRender;
let targetFPS = 40;
let frameInterval = 1 / targetFPS;

function preload() {
glApp.preload(
{
canvas: document.getElementById('canvas'),
orbitTarget: document.getElementById('canvas'),
ASSETS_PATH: '/assets/',
},
() => {
init();
}
);
}

function init() {
glApp.init();
time = performance.now() / 1000;
lastRender = time;
window.addEventListener('resize', onResize);

onResize();
animate();
}

function animate() {
let newTime = performance.now() / 1000;
let dt = newTime - time;
if (newTime - lastRender >= frameInterval) {
lastRender = newTime;
update(dt);
time = newTime;
}

requestAnimationFrame(animate);
}

function update(dt) {
glApp.render(dt);
}

function onResize() {
const sidebarOffset = 348 + 20; // sidebar + padding
glApp.properties.cameraOffsetX = sidebarOffset / window.innerWidth;
glApp.setSize(window.innerWidth, window.innerHeight);
}

document.addEventListener('DOMContentLoaded', () => {
if (!window.WebGL2RenderingContext && !window.WebGLRenderingContext) {
console.error('WebGL not supported!');
return;
}

preload();
});
</script>
</main>
<div id="root"></div>
<script type="module" src="src/main.tsx"></script>
</body>
</html>
207 changes: 116 additions & 91 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"dependencies": {
"@floating-ui/react": "^0.27.4",
"@lottiefiles/dotlottie-react": "^0.13.0",
"@tari-project/tari-tower": "^0.0.16",
"@tauri-apps/api": "^2.2.0",
"@tauri-apps/plugin-clipboard-manager": "^2.2.0",
"@tauri-apps/plugin-os": "^2.2.0",
Expand Down
4,732 changes: 0 additions & 4,732 deletions public/assets/glApp.js

This file was deleted.

Binary file removed public/assets/models/BASE.buf
Binary file not shown.
Binary file removed public/assets/models/BOX.buf
Binary file not shown.
Binary file removed public/assets/models/COIN.buf
Binary file not shown.
Binary file removed public/assets/models/COIN_PLACEMENT.buf
Binary file not shown.
Binary file removed public/assets/models/lose_animation.buf
Binary file not shown.
Binary file removed public/assets/textures/LDR_RGB1_0.png
Binary file not shown.
8 changes: 0 additions & 8 deletions public/assets/textures/LICENSES.txt

This file was deleted.

Binary file removed public/assets/textures/gobo.jpg
Binary file not shown.
Binary file removed public/assets/textures/matcap_gold.jpg
Binary file not shown.
4 changes: 3 additions & 1 deletion src/App/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ const CurrentAppSection = memo(function CurrentAppSection() {
});

export default function App() {
const isAppReady = useIsAppReady();
const isShuttingDown = useShuttingDown();
const setError = useAppStateStore((s) => s.setError);
const setIsWebglNotSupported = useUIStore((s) => s.setIsWebglNotSupported);

Expand All @@ -75,7 +77,7 @@ export default function App() {
return (
<ThemeProvider>
<GlobalReset />
<GlobalStyle />
<GlobalStyle $hideCanvas={!isAppReady || isShuttingDown} />
<LazyMotion features={domAnimation} strict>
<FloatingElements />
<CurrentAppSection />
Expand Down
1 change: 1 addition & 0 deletions src/components/elements/ToggleSwitch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const Wrapper = styled.label<{ $disabled?: boolean }>`
${({ $disabled }) =>
$disabled &&
css`
cursor: auto;
pointer-events: none;
opacity: 0.8;
`}
Expand Down
51 changes: 41 additions & 10 deletions src/containers/main/Dashboard/components/VisualMode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import styled from 'styled-components';
import { ToggleSwitch } from '@app/components/elements/ToggleSwitch.tsx';
import { useAppConfigStore } from '@app/store/useAppConfigStore';
import { useUIStore } from '@app/store/useUIStore';
import { setVisualMode, useAppConfigStore } from '@app/store/useAppConfigStore';
import { sidebarTowerOffset, TOWER_CANVAS_ID, useUIStore } from '@app/store/useUIStore';
import { Typography } from '@app/components/elements/Typography';
import {
SettingsGroup,
Expand All @@ -13,23 +13,51 @@ import {
SettingsGroupWrapper,
} from '@app/containers/floating/Settings/components/SettingsGroup.styles';

import { loadTowerAnimation, removeTowerAnimation, setAnimationState } from '@tari-project/tari-tower';

const ErrorTypography = styled(Typography)(({ theme }) => ({
color: theme.palette.error.main,
}));

function VisualMode() {
const visualMode = useAppConfigStore((s) => s.visual_mode);
const setVisualMode = useAppConfigStore((s) => s.setVisualMode);
const visualModeToggleLoading = useAppConfigStore((s) => s.visualModeToggleLoading);
const isWebglNotSupported = useUIStore((s) => s.isWebglNotSupported);
const { t } = useTranslation('settings', { useSuspense: false });

const handleDisable = useCallback(() => {
setVisualMode(false);
removeTowerAnimation({ canvasId: TOWER_CANVAS_ID })
.then(() => {
// Force garbage collection to clean up WebGL context
if (window.gc) {
window.gc();
}
})
.catch((e) => {
console.error('Could not disable visual mode. Error at loadTowerAnimation:', e);
setVisualMode(true);
});
}, []);
const handleEnable = useCallback(() => {
loadTowerAnimation({ canvasId: TOWER_CANVAS_ID, offset: sidebarTowerOffset })
.then(() => {
setVisualMode(true);
setAnimationState('showVisual');
})
.catch((e) => {
console.error('Could not enable visual mode. Error at loadTowerAnimation:', e);
setVisualMode(false);
});
}, []);

const handleSwitch = useCallback(() => {
const canvasElement = document.getElementById('canvas');
if (canvasElement) {
canvasElement.style.display = visualMode ? 'none' : 'block';
if (visualMode) {
handleDisable();
} else {
handleEnable();
}
setVisualMode(!visualMode);
}, [setVisualMode, visualMode]);
}, [handleDisable, handleEnable, visualMode]);

return (
<SettingsGroupWrapper>
Expand All @@ -41,11 +69,14 @@ function VisualMode() {
{isWebglNotSupported && <ErrorTypography color="error">{t('webgl-not-supported')}</ErrorTypography>}
</SettingsGroupContent>
<SettingsGroupAction style={{ alignItems: 'center' }}>
<ToggleSwitch disabled={isWebglNotSupported} checked={visualMode} onChange={handleSwitch} />
<ToggleSwitch
disabled={visualModeToggleLoading || isWebglNotSupported}
checked={visualMode}
onChange={() => handleSwitch()}
/>
</SettingsGroupAction>
</SettingsGroup>
</SettingsGroupWrapper>
);
}

export default VisualMode;
72 changes: 0 additions & 72 deletions src/glApp.d.ts

This file was deleted.

10 changes: 0 additions & 10 deletions src/global.d.ts

This file was deleted.

42 changes: 33 additions & 9 deletions src/hooks/mining/useMiningUiStateMachine.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,57 @@
import { useAppStateStore } from '@app/store/appStateStore';
import { useMiningStore } from '@app/store/useMiningStore';
import { setAnimationState } from '@app/visuals';
import { useEffect } from 'react';
import { setAnimationState, animationStatus } from '@tari-project/tari-tower';
import { useMiningMetricsStore } from '@app/store/useMiningMetricsStore.ts';
import { useAppConfigStore } from '@app/store/useAppConfigStore.ts';

export const useUiMiningStateMachine = () => {
const isMiningInitiated = useMiningStore((s) => s.miningInitiated);
const isChangingMode = useMiningStore((s) => s.isChangingMode);
const cpuIsMining = useMiningMetricsStore((s) => s.cpu_mining_status.is_mining);
const gpuIsMining = useMiningMetricsStore((s) => s.gpu_mining_status.is_mining);
const setupComplete = useAppStateStore((s) => s.setupComplete);
const visualMode = useAppConfigStore((s) => s.visual_mode);
const visualModeToggleLoading = useAppConfigStore((s) => s.visualModeToggleLoading);
const isMining = cpuIsMining || gpuIsMining;

const statusIndex = window?.glApp?.stateManager?.statusIndex;
const indexTrigger = animationStatus;

useEffect(() => {
const status = window?.glApp?.stateManager?.status;
const notStarted = !status || status == 'not-started' || status == 'stop';
let isLatestEffect = true;
if (!visualMode || visualModeToggleLoading) return;
const notStarted = !animationStatus || animationStatus == 'not-started';
if (isMining && notStarted) {
setAnimationState('start');
// Debounce animation state changes
const timer = setTimeout(() => {
if (isLatestEffect) {
setAnimationState('start');
}
}, 300);
return () => {
clearTimeout(timer);
isLatestEffect = false;
};
}
}, [statusIndex, isMining]);
}, [indexTrigger, isMining, visualMode, visualModeToggleLoading]);

useEffect(() => {
const notStopped = window?.glApp?.stateManager?.status !== 'not-started';
let isLatestEffect = true;
if (!visualMode || visualModeToggleLoading) return;
const notStopped = animationStatus !== 'not-started';
const preventStop = !setupComplete || isMiningInitiated || isChangingMode;
const shouldStop = !isMining && notStopped && !preventStop;
if (shouldStop) {
setAnimationState('stop');
// Debounce animation state changes
const timer = setTimeout(() => {
if (isLatestEffect) {
setAnimationState('stop');
}
}, 300);
return () => {
clearTimeout(timer);
isLatestEffect = false;
};
}
}, [statusIndex, setupComplete, isMiningInitiated, isMining, isChangingMode]);
}, [indexTrigger, setupComplete, isMiningInitiated, isMining, isChangingMode, visualMode, visualModeToggleLoading]);
};
7 changes: 6 additions & 1 deletion src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,13 @@ import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import AppWrapper from './App/AppWrapper.tsx';

const root = createRoot(document.getElementById('root') as HTMLElement);
document.addEventListener('DOMContentLoaded', () => {
if (!window.WebGL2RenderingContext && !window.WebGLRenderingContext) {
console.error('WebGL not supported!');
}
});

const root = createRoot(document.getElementById('root') as HTMLElement);
root.render(
<StrictMode>
<AppWrapper />
Expand Down
Loading

0 comments on commit 333aa88

Please sign in to comment.