"CandyCAD.MainWindow"

OpenTK 2026-05-07 17:45:38

<Window x:Class="CandyCAD.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:CandyCAD"
        Title="CandyCAD - 3D Viewer" 
        Height="600" Width="900"
        Loaded="Window_Loaded"
        Closing="Window_Closing">

    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="200"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>

        <!-- 左侧控制面板 -->
        <Border Grid.Column="0" Background="#2A2A2A" BorderBrush="#444" BorderThickness="0,0,1,0">
            <StackPanel Margin="10">

                <TextBlock Text="CandyCAD" FontSize="18" FontWeight="Bold" Foreground="#F0D84C" 
                           HorizontalAlignment="Center" Margin="0,0,0,15"/>

                <Border Background="#3A3A3A" CornerRadius="4" Padding="8" Margin="0,0,0,10">
                    <StackPanel>
                        <TextBlock Text="当前场景" Foreground="#CCC" FontWeight="Bold" Margin="0,0,0,5"/>
                        <TextBlock x:Name="SceneInfo" Text="立方体" Foreground="#888" FontSize="11"/>
                        <TextBlock x:Name="VertexInfo" Text="顶点: 36" Foreground="#888" FontSize="11"/>
                        <TextBlock x:Name="TriangleInfo" Text="三角形: 12" Foreground="#888" FontSize="11"/>
                    </StackPanel>
                </Border>

                <GroupBox Header="相机控制" Foreground="#CCC" Margin="0,0,0,10">
                    <StackPanel>
                        <Button x:Name="ResetCameraBtn" Content="重置相机" Height="30" Margin="0,3" 
                                Click="ResetCamera_Click" Background="#4A6A8A" Foreground="White" BorderThickness="0"/>
                        <Button x:Name="FitViewBtn" Content="适应视图" Height="30" Margin="0,3" 
                                Click="FitView_Click" Background="#4A6A8A" Foreground="White" BorderThickness="0"/>
                    </StackPanel>
                </GroupBox>

                <GroupBox Header="模型变换" Foreground="#CCC" Margin="0,0,0,10">
                    <StackPanel>
                        <Slider x:Name="RotateSlider" Minimum="0" Maximum="360" Value="45" 
                                TickFrequency="45" TickPlacement="BottomRight"
                                ValueChanged="RotateSlider_Changed"/>
                        <TextBlock Text="旋转角度" Foreground="#AAA" FontSize="11" HorizontalAlignment="Center"/>

                        <Slider x:Name="ScaleSlider" Minimum="0.5" Maximum="2.0" Value="1.0" 
                                TickFrequency="0.25" TickPlacement="BottomRight" Margin="0,10,0,0"
                                ValueChanged="ScaleSlider_Changed"/>
                        <TextBlock Text="缩放" Foreground="#AAA" FontSize="11" HorizontalAlignment="Center"/>
                    </StackPanel>
                </GroupBox>

                <GroupBox Header="渲染设置" Foreground="#CCC" Margin="0,0,0,10">
                    <StackPanel>
                        <CheckBox x:Name="WireframeCheckBox" Content="线框模式" Foreground="#DDD" Margin="0,3" 
                                  Checked="WireframeMode_Changed" Unchecked="WireframeMode_Changed"/>
                        <CheckBox x:Name="GridCheckBox" Content="显示网格" Foreground="#DDD" Margin="0,3" 
                                  IsChecked="True" Checked="GridMode_Changed" Unchecked="GridMode_Changed"/>
                    </StackPanel>
                </GroupBox>

                <TextBlock x:Name="InfoText" Text="鼠标拖拽旋转视图" Foreground="#888" FontSize="11" 
                           HorizontalAlignment="Center" Margin="0,10,0,0"/>

                <TextBlock x:Name="FpsText" Text="FPS: --" Foreground="#F0D84C" FontSize="12" 
                           HorizontalAlignment="Center" Margin="0,10,0,0"/>
            </StackPanel>
        </Border>

        <!-- 右侧渲染区域 -->
        <Border Grid.Column="1" Background="#1E1E1E">
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="*"/>
                    <RowDefinition Height="25"/>
                </Grid.RowDefinitions>

                <!-- OpenGL 渲染视图 -->
                <Border Grid.Column="1" Background="#1E1E1E" Margin="5">
                    <WindowsFormsHost x:Name="host">
                        <local:OpenGLControl x:Name="glView" 
                             BackColor="#1E1E1E" 
                             Dock="Fill" 
                             MinimumSize="400,300" />
                    </WindowsFormsHost>
                </Border>

                <!-- 状态栏 -->
                <StatusBar Grid.Row="1" Background="#2A2A2A">
                    <StatusBarItem>
                        <TextBlock x:Name="StatusText" Text="就绪" Foreground="#888"/>
                    </StatusBarItem>
                    <Separator/>
                    <StatusBarItem>
                        <TextBlock x:Name="CameraPosText" Text="相机: (0, 0, 5)" Foreground="#888"/>
                    </StatusBarItem>
                    <Separator/>
                    <StatusBarItem>
                        <TextBlock x:Name="MousePosText" Text="鼠标: (0, 0)" Foreground="#888"/>
                    </StatusBarItem>
                </StatusBar>
            </Grid>
        </Border>
    </Grid>
</Window>

using System;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;

namespace CandyCAD
{
    public partial class MainWindow : Window
    {
        private int frameCount = 0;
        private DateTime lastTime = DateTime.Now;
        private System.Windows.Point lastMousePos;
        private bool isMouseDown = false;

        public MainWindow()
        {
            InitializeComponent();

            // 在 glView 上直接订阅鼠标事件(WinForms 事件)
            glView.MouseDown += OnGlViewMouseDown;
            glView.MouseMove += OnGlViewMouseMove;
            glView.MouseUp += OnGlViewMouseUp;
            glView.MouseWheel += OnGlViewMouseWheel;
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            CompositionTarget.Rendering += OnRendering;

            if (StatusText != null)
                StatusText.Text = "CandyCAD 已启动 | 左键旋转 | 右键平移 | 滚轮缩放";
        }

        private void OnRendering(object sender, EventArgs e)
        {
            if (glView != null)
            {
                RenderInterop.RenderFrame();
            }

            frameCount++;
            var now = DateTime.Now;
            if ((now - lastTime).TotalSeconds >= 1)
            {
                if (FpsText != null)
                    FpsText.Text = $"FPS: {frameCount}";
                frameCount = 0;
                lastTime = now;
            }

            RenderInterop.GetCameraPosition(out float x, out float y, out float z);
            if (CameraPosText != null)
                CameraPosText.Text = $"相机: ({x:F1}, {y:F1}, {z:F1})";
        }

        // ===== WinForms 鼠标事件(直接使用 glView)=====
        private void OnGlViewMouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
        {
            lastMousePos = new System.Windows.Point(e.X, e.Y);
            isMouseDown = true;
            glView.Capture = true;  // WinForms 的 Capture 属性

            if (StatusText != null)
            {
                if (e.Button == System.Windows.Forms.MouseButtons.Left)
                    StatusText.Text = "旋转视图 (左键拖拽)";
                else if (e.Button == System.Windows.Forms.MouseButtons.Right)
                    StatusText.Text = "平移视图 (右键拖拽)";
            }
        }

        private void OnGlViewMouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
        {
            if (!isMouseDown) return;

            double dx = e.X - lastMousePos.X;
            double dy = e.Y - lastMousePos.Y;

            if (MousePosText != null)
                MousePosText.Text = $"鼠标: ({e.X}, {e.Y})";

            if (e.Button == System.Windows.Forms.MouseButtons.Left)
            {
                RenderInterop.ProcessMouseMovement((float)dx, (float)dy);
            }
            else if (e.Button == System.Windows.Forms.MouseButtons.Right)
            {
                RenderInterop.ProcessPan((float)dx, (float)dy);
            }

            lastMousePos = new System.Windows.Point(e.X, e.Y);
        }

        private void OnGlViewMouseUp(object sender, System.Windows.Forms.MouseEventArgs e)
        {
            isMouseDown = false;
            glView.Capture = false;  // WinForms 的 Capture 属性

            if (StatusText != null)
                StatusText.Text = "就绪 | 左键旋转 | 右键平移 | 滚轮缩放";
        }

        private void OnGlViewMouseWheel(object sender, System.Windows.Forms.MouseEventArgs e)
        {
            float delta = e.Delta > 0 ? 1.0f : -1.0f;
            RenderInterop.ProcessMouseScroll(delta);

            if (StatusText != null)
                StatusText.Text = e.Delta > 0 ? "放大" : "缩小";
        }

        // ===== 按钮事件 =====
        private void ResetCamera_Click(object sender, RoutedEventArgs e)
        {
            RenderInterop.ResetCamera();
            if (StatusText != null)
                StatusText.Text = "相机已重置";
        }

        private void FitView_Click(object sender, RoutedEventArgs e)
        {
            RenderInterop.FitView();
            if (StatusText != null)
                StatusText.Text = "适应视图";
        }

        private void RotateSlider_Changed(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            float angle = (float)e.NewValue;
            RenderInterop.SetModelRotation(angle, angle, 0);
            if (InfoText != null)
                InfoText.Text = $"旋转角度: {angle:F0}°";
        }

        private void ScaleSlider_Changed(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            float scale = (float)e.NewValue;
            RenderInterop.SetModelScale(scale);
            if (InfoText != null)
                InfoText.Text = $"缩放: {scale:F2}";
        }

        private void WireframeMode_Changed(object sender, RoutedEventArgs e)
        {
            bool enabled = WireframeCheckBox.IsChecked == true;
            RenderInterop.SetWireframeMode(enabled);
            if (StatusText != null)
                StatusText.Text = enabled ? "线框模式" : "实体模式";
        }

        private void GridMode_Changed(object sender, RoutedEventArgs e)
        {
            bool show = GridCheckBox.IsChecked == true;
            RenderInterop.ShowGrid(show);
        }

        private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            RenderInterop.Shutdown();
        }
    }
}

using System;
using System.Windows.Forms;

namespace CandyCAD
{
    public class OpenGLControl : Control
    {

        public OpenGLControl()
        {
            SetStyle(ControlStyles.EnableNotifyMessage, true);
            this.MouseWheel += (s, e) => { };
        }
        private bool isInitialized = false;

        protected override void OnHandleCreated(EventArgs e)
        {
            base.OnHandleCreated(e);

            System.Diagnostics.Debug.WriteLine("=== OnHandleCreated called ===");
            System.Diagnostics.Debug.WriteLine($"Handle: {this.Handle}, Width: {this.Width}, Height: {this.Height}");

            if (!isInitialized && this.IsHandleCreated && this.Width > 0 && this.Height > 0)
            {
                System.Diagnostics.Debug.WriteLine("Calling RenderInterop.Initialize...");
                bool result = RenderInterop.Initialize(this.Handle, this.Width, this.Height);
                System.Diagnostics.Debug.WriteLine($"RenderInterop.Initialize returned: {result}");

                if (!result)
                    throw new Exception("Failed to initialize OpenGL renderer");

                isInitialized = true;
                System.Diagnostics.Debug.WriteLine("OpenGL initialized successfully");
            }
            else
            {
                System.Diagnostics.Debug.WriteLine("Skipping initialization - conditions not met");
                System.Diagnostics.Debug.WriteLine($"isInitialized: {isInitialized}");
                System.Diagnostics.Debug.WriteLine($"IsHandleCreated: {this.IsHandleCreated}");
                System.Diagnostics.Debug.WriteLine($"Width: {this.Width}, Height: {this.Height}");
            }
        }

        protected override void OnResize(EventArgs e)
        {
            base.OnResize(e);
            if (isInitialized && this.Width > 0 && this.Height > 0)
            {
                RenderInterop.Resize(this.Width, this.Height);
            }
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            if (isInitialized)
                RenderInterop.RenderFrame();
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing && isInitialized)
                RenderInterop.Shutdown();
            base.Dispose(disposing);
        }
    }
}

using System;
using System.Runtime.InteropServices;

namespace CandyCAD
{
    public static class RenderInterop
    {
        private const string DllName = "CandyRenderEngine.dll";

        // 基础渲染
        [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
        public static extern bool Initialize(IntPtr hwnd, int width, int height);

        [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
        public static extern void Shutdown();

        [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
        public static extern void RenderFrame();

        [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
        public static extern void Resize(int width, int height);

        // 相机控制
        [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
        public static extern void ProcessMouseMovement(float xOffset, float yOffset);

        [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
        public static extern void ProcessMouseScroll(float yOffset);

        [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
        public static extern void ProcessPan(float xOffset, float yOffset);

        [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
        public static extern void ResetCamera();

        [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
        public static extern void FitView();

        // 模型变换
        [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
        public static extern void SetModelRotation(float x, float y, float z);

        [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
        public static extern void SetModelScale(float scale);

        [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
        public static extern void SetModelPosition(float x, float y, float z);

        // 渲染设置
        [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
        public static extern void SetWireframeMode(bool enabled);

        [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
        public static extern void ShowGrid(bool show);

        // 获取信息
        [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
        public static extern void GetCameraPosition(out float x, out float y, out float z);
        // 添加到 RenderInterop.cs 中

        [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
        public static extern bool GetWireframeMode();

        [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
        public static extern bool IsGridVisible();
    }
}

...全文
9 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

3

社区成员

发帖
与我相关
我的任务
社区描述
openTK、OpenGL、WebGL技术学习交流
图形渲染c#程序人生 技术论坛(原bbs) 广东省·深圳市
社区管理员
  • 亿只小灿灿
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧