ios-ARKit 實(shí)現(xiàn)與3D模型交互(模型換膚挠说、運(yùn)動(dòng))

本章實(shí)現(xiàn)對(duì)模型(Demo中用了一個(gè)汽車模型)的交互操作澡谭,包括對(duì)汽車模型換膚、零件拆卸损俭、輪胎運(yùn)轉(zhuǎn)蛙奖、后視鏡開(kāi)合、以及車窗的升降等等杆兵。在與AR世界的交互之前對(duì)AR世界的構(gòu)建雁仲,以及模型的展示在另一篇文章中(AR世界的構(gòu)建:http://www.reibang.com/p/f7c26b058348)這里就不再講述。

構(gòu)建出AR世界并且在AR世界中展示3D模型后就可以開(kāi)始對(duì)模型進(jìn)行各種操作及交互:
思考:如何實(shí)現(xiàn)對(duì)汽車模型或者其身上子模型部件進(jìn)行操作琐脏?
一個(gè)復(fù)雜模型的制作原理是由多個(gè)材質(zhì)球或者模型(SceneKit中的節(jié)點(diǎn)SCNNode)拼接而成的的攒砖。在SceneKit中我們可以通過(guò)檢索模型的名稱對(duì)其進(jìn)行交互。

Demo中相關(guān)屬性:列出以便文章閱讀

@property (nonatomic, strong) UIButton *backButton;//返回按鈕
@property (nonatomic, strong) ARSCNView *sceneView;//AR視圖(AR場(chǎng)景填在在其上)
@property (nonatomic, strong) ARWorldTrackingConfiguration *configuration;//AR世界追蹤
@property (nonatomic, strong) SCNScene *scene;//AR場(chǎng)景

@property (nonatomic, strong) ARPlaneAnchor *planAnchor;//平面錨點(diǎn)
@property (nonatomic, strong) SCNNode *planParanNode;//地面節(jié)點(diǎn)(模型放上面)
@property (nonatomic, assign) BOOL modelShowing;//是否已經(jīng)顯示模型(已經(jīng)顯示模型后不繼續(xù)重新布置平面)
@property (nonatomic, assign) BOOL isSeachPlan;//是否已經(jīng)找到平面

@property (nonatomic, strong) SCNNode *carModelNode;//汽車模型節(jié)點(diǎn)

@property (nonatomic, assign) BOOL tireSpared;//是否已經(jīng)拆下輪胎

//顏色面板
@property (nonatomic, strong) HCColorPanelView *colorPanelView;
//菜單面板
@property (nonatomic, strong) UIButton *menuButton;
@property (nonatomic, strong) HCMenuPanelView *menuPanelView;

·碰撞檢測(cè) (點(diǎn)擊手機(jī)屏幕日裙,檢測(cè)是否點(diǎn)擊了模型)

給汽車模型起個(gè)名字:

self.carModelNode.name = @"modelCarNode";//很重要吹艇,根據(jù)這個(gè)那么做對(duì)比,是否點(diǎn)擊了模型

點(diǎn)擊屏幕后監(jiān)聽(tīng)- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event方法昂拂,遍歷點(diǎn)擊事件受神,檢測(cè)是否與模型進(jìn)行了碰撞:

//點(diǎn)擊檢測(cè)(碰撞檢測(cè))
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    if (self.arType == ARWorldTrackingConfigurationType_planeDetection_CarDemo && self.modelShowing) {
        //已經(jīng)放置了汽車模型,檢測(cè)點(diǎn)擊汽車事件
        UITouch *touch = [touches anyObject];
        CGPoint tapPoint  = [touch locationInView:self.sceneView];//該點(diǎn)就是手指的點(diǎn)擊位置
        NSDictionary *hitTestOptions = [NSDictionary dictionaryWithObjectsAndKeys:@(true),SCNHitTestBoundingBoxOnlyKey, nil];
        NSArray<SCNHitTestResult *> * results= [self.sceneView hitTest:tapPoint options:hitTestOptions];
        for (SCNHitTestResult *res in results) {//遍歷所有的返回結(jié)果中的node
            if ([self isNodeCarModelObject:res.node]) {
//                [[HCToast shareInstance] showToast:@"點(diǎn)擊了汽車"];
                NSLog(@"點(diǎn)擊了汽車模型...............");
                break;
            }
        }
    }
}

//上溯找尋指定的node(是否點(diǎn)擊了汽車)
-(BOOL) isNodeCarModelObject:(SCNNode*)node {
    if ([@"modelCarNode" isEqualToString:node.name]) {
        return true;
    }
    if (node.parentNode != nil) {
        return [self isNodeCarModelObject:node.parentNode];
    }
    return false;
}

·給汽車模型換膚

給模型換膚原理就是修改汽車模型的材質(zhì)貼圖格侯,那么久同樣需要找到汽車車身的模型:


車身模型.png

上圖中鼻听,我們可以打開(kāi)汽車模型,選中車身联四,從左側(cè)的模型列表中可以看到撑碴,車身的模型名稱為“body_01”,那么我們就先去除“body_01”的SCNNode節(jié)點(diǎn)朝墩。

//修改汽車顏色
            SCNNode *bodyNode = [weakSelf.carModelNode childNodeWithName:@"body_01" recursively:YES];
            bodyNode.childNodes[0].geometry.firstMaterial.diffuse.contents = color;//這里的顏色值可以設(shè)置純色或者設(shè)置圖片醉拓。這樣就達(dá)到了給汽車換皮膚的功能。

汽車換膚效果:


換膚.gif

·雙指捏合縮放模型、拖拽旋轉(zhuǎn)模型

首先捏合廉嚼、拖拽就需要用到手勢(shì)玫镐,給SCNView添加手勢(shì)
縮放模型原理:當(dāng)捏合開(kāi)始時(shí),記錄開(kāi)始捏合時(shí)模型的縮放比例怠噪,然后在手勢(shì)變化的過(guò)程中計(jì)算當(dāng)前手勢(shì)scale除以手勢(shì)開(kāi)始時(shí)的scale, 以開(kāi)始時(shí)模型的scale為基準(zhǔn)相乘, 實(shí)現(xiàn)圓潤(rùn)的放大縮小效果。這個(gè)比例大小可以自己調(diào)整杜跷,以達(dá)到自己理想的縮放范圍傍念。

//給場(chǎng)景視圖添加手勢(shì)
- (void)addRecognizerToSceneView{
    UIPanGestureRecognizer *panGes = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panView:)];
    [self.sceneView addGestureRecognizer:panGes];
    UIPinchGestureRecognizer *pinchGes = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(pinchView:)];
    [self.sceneView addGestureRecognizer:pinchGes];
}

監(jiān)聽(tīng)手勢(shì)觸發(fā)方法

// 處理拖拉手勢(shì) - 移動(dòng) 旋轉(zhuǎn)
- (void)panView:(UIPanGestureRecognizer *)panGestureRecognizer{
    if (self.modelShowing) {
        NSLog(@"拖拽.....................");
        UIView *view = panGestureRecognizer.view;
        CGPoint location = [panGestureRecognizer translationInView:self.sceneView];
        CGPoint velocityPoint = [panGestureRecognizer velocityInView:self.sceneView];
        switch (panGestureRecognizer.state) {
            case UIGestureRecognizerStateChanged:{
                
                //旋轉(zhuǎn)模型
                float xx = velocityPoint.x/5000;
                float yy = velocityPoint.y/5000;
                self.carModelNode.eulerAngles = SCNVector3Make(0, self.carModelNode.eulerAngles.y + (fabs(xx) > fabs(yy) ? xx : -yy), 0);
                
                break;
            }
            case UIGestureRecognizerStateEnded:{
                return;
            }
                
            default:{
                break;
            }
        }
    }
    
}

// 處理縮放手勢(shì)
CGFloat oldGesScale = Car_Model_Scale;
CGFloat oldModelScale = Car_Model_Scale;
- (void)pinchView:(UIPinchGestureRecognizer *)pinchGestureRecognizer{
    if (self.modelShowing){
//        NSLog(@"縮放.....................");
        if (pinchGestureRecognizer.state == UIGestureRecognizerStateBegan) {//手勢(shì)開(kāi)始
            oldGesScale = pinchGestureRecognizer.scale;//手勢(shì)開(kāi)始時(shí),獲取模型的比例
            oldModelScale = self.carModelNode.scale.x;//手勢(shì)開(kāi)始時(shí)葛闷,獲取模型的scale
        }
        
        if (pinchGestureRecognizer.state == UIGestureRecognizerStateChanged) {
            //計(jì)算, 當(dāng)前手勢(shì)scale除以手勢(shì)開(kāi)始時(shí)的scale, 以開(kāi)始時(shí)模型的scale為基準(zhǔn)相乘, 實(shí)現(xiàn)圓潤(rùn)的放大縮小效果
            CGFloat currentGesScale = pinchGestureRecognizer.scale;
            CGFloat scale = oldModelScale *  (float)(currentGesScale / oldGesScale);
            scale = scale < 0.005 ? 0.005 : scale;
            scale = scale > 0.05 ? 0.05 : scale ;
            self.carModelNode.scale = SCNVector3Make(scale, scale, scale);
        }
        
    }
}

·汽車零件拆卸 - 拆卸輪胎

零件拆卸原理:同樣需要從模型中讀取輪胎的模型憋槐,同樣可以再模型中查看輪胎的模型名稱。輪胎模型又是由許多小零件組成淑趾,一般模型師會(huì)將其放在一個(gè)組內(nèi)阳仔,組成一個(gè)輪胎模型:如下圖:輪胎模型組為"Group002"


輪胎模型.png

拿到輪胎模型后,進(jìn)行拆卸動(dòng)作:將模型進(jìn)行位移和旋轉(zhuǎn)扣泊,造成輪胎與車身存在位置與角度的差別近范,從而實(shí)現(xiàn)輪胎(或其他零件)拆卸的功能。
同理延蟹,零件復(fù)原可以將拆卸下的零件經(jīng)過(guò)位移和旋轉(zhuǎn)進(jìn)行復(fù)位评矩。
零件拆卸和復(fù)位方法:

/**
拆汽車零件 

@param sparePartsName 汽車零件模型名稱
@param spareDistance 拆卸偏離距離 (為0時(shí),使用默認(rèn)距離)
@param beFlip 是否翻轉(zhuǎn)模型
*/
- (void)removePartsCar:(NSString *)sparePartsName spareDistance:(CGFloat)spareDistance beFlip:(BOOL)beFlip{
   for (SCNNode *partsNode in self.carModelNode.childNodes) {
       if ([partsNode.name isEqualToString:sparePartsName]) {
           //找到對(duì)應(yīng)的零件模型
           [UIView animateWithDuration:1.0 animations:^{
               //零件往外移動(dòng)
               partsNode.position = SCNVector3Make(partsNode.position.x + spareDistance ,partsNode.position.y,partsNode.position.z);
           } completion:^(BOOL finished) {
               if (beFlip) {
                   //零件翻轉(zhuǎn)
                   partsNode.eulerAngles = SCNVector3Make(0, 0, M_PI/2);
               }
           }];
           
       }
   }
}

/**
安裝拆下的零件

@param sparePartsName 汽車零件模型名稱
@param spareDistance 拆卸偏離距離 (為0時(shí)阱飘,使用默認(rèn)距離)
@param beFlip 是否翻轉(zhuǎn)模型
*/
- (void)recoveryPartsCar:(NSString *)sparePartsName spareDistance:(CGFloat)spareDistance beFlip:(BOOL)beFlip{
   for (SCNNode *partsNode in self.carModelNode.childNodes) {
       if ([partsNode.name isEqualToString:sparePartsName]) {
           //找到對(duì)應(yīng)的零件模型 Group002:左前輪
           [UIView animateWithDuration:1.0 animations:^{
               if (beFlip) {
                   //零件翻轉(zhuǎn)
                   partsNode.eulerAngles = SCNVector3Make(0, 0, 0);
               }
           } completion:^(BOOL finished) {
               //零件回到原來(lái)位置
               partsNode.position = SCNVector3Make(partsNode.position.x - spareDistance ,partsNode.position.y,partsNode.position.z);
           }];
           
       }
   }
}

拆卸零件:


拆卸零件.gif

·輪胎運(yùn)轉(zhuǎn)

拿到四個(gè)輪胎模型后斥杜,對(duì)齊進(jìn)行旋轉(zhuǎn)。正常邏輯沥匈,輪胎旋轉(zhuǎn)是繞X軸進(jìn)行無(wú)限循環(huán)轉(zhuǎn)動(dòng)蔗喂。使用貝塞爾動(dòng)畫(CABasicAnimation)進(jìn)行旋轉(zhuǎn):

//開(kāi)始輪胎轉(zhuǎn)動(dòng)
- (void)startTireTurnningModel:(NSString *)modelName duration:(NSTimeInterval)duration{
    for (SCNNode *partsNode in self.carModelNode.childNodes) {
        if ([partsNode.name isEqualToString:modelName]) {
            //創(chuàng)建自轉(zhuǎn)動(dòng)畫
            CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"rotation"];
            animation.duration = duration;
            animation.toValue = [NSValue valueWithSCNVector4:SCNVector4Make(0,1,0, M_PI *2)];
            animation.repeatCount = FLT_MAX;
            [partsNode addAnimation:animation forKey:@"tire rotation"];
            [partsNode runAction:[SCNAction repeatActionForever:[SCNAction rotateByX:2 y:0 z:0 duration:duration]]];//輪胎自轉(zhuǎn) 繞X軸自轉(zhuǎn)
        }
    }
}

想要停止輪胎轉(zhuǎn)動(dòng),移除其動(dòng)畫:

//停止輪胎轉(zhuǎn)動(dòng)
- (void)stopTireTurnningModel:(NSString *)modelName{
    for (SCNNode *partsNode in self.carModelNode.childNodes) {
        if ([partsNode.name isEqualToString:modelName]) {
            //需要同時(shí)remove Animation和Actions高帖,只移除其中一個(gè)無(wú)效
            [partsNode removeAnimationForKey:@"tire rotation"];
            [partsNode removeAllActions];
        }
    }
}

輪胎運(yùn)轉(zhuǎn)效果:


hou

·后視鏡折疊與車窗升降效果的實(shí)現(xiàn):

后視鏡開(kāi)合的原理與輪胎轉(zhuǎn)動(dòng)的原理是一樣的缰儿,位移的差別就是圍繞的旋轉(zhuǎn)軸(后視鏡圍繞Y軸旋轉(zhuǎn),右手坐標(biāo)系)棋恼、旋轉(zhuǎn)角度返弹、旋轉(zhuǎn)次數(shù)不一樣:

//合上后視鏡
- (void)closeRearviewMirrorModel:(NSString *)modelName angle:(CGFloat)angle Duration:(NSTimeInterval)duration{
    for (SCNNode *partsNode in self.carModelNode.childNodes) {
        if ([partsNode.name isEqualToString:modelName]) {
            
            //創(chuàng)建自轉(zhuǎn)動(dòng)畫
            CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"rotation"];//執(zhí)行的是旋轉(zhuǎn)
            animation.duration = duration;
//            animation.toValue = [NSValue valueWithSCNVector4:SCNVector4Make(0,0,0, 0)];//旋轉(zhuǎn)角度
            animation.repeatCount = 1;
            [partsNode addAnimation:animation forKey:@"rearviewMirror rotation"];
            [partsNode runAction:[SCNAction repeatAction:[SCNAction rotateByX:0 y:angle z:0 duration:duration] count:1]];//后視鏡繞Y軸旋轉(zhuǎn) angle角度
        }
    }
}

//打開(kāi)后視鏡
- (void)openRearviewMirrorModel:(NSString *)modelName angle:(CGFloat)angle Duration:(NSTimeInterval)duration{
    for (SCNNode *partsNode in self.carModelNode.childNodes) {
        if ([partsNode.name isEqualToString:modelName]) {
            //創(chuàng)建自轉(zhuǎn)動(dòng)畫
            CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"rotation"];
            animation.duration = duration;
//            animation.toValue = [NSValue valueWithSCNVector4:SCNVector4Make(0,1,0, 0)];
            animation.repeatCount = 1;
            [partsNode addAnimation:animation forKey:@"rearviewMirror2 rotation"];
            [partsNode runAction:[SCNAction repeatAction:[SCNAction rotateByX:0 y:angle z:0 duration:duration] count:1]];//后視鏡繞Y軸旋轉(zhuǎn)
        }
    }
}

后視鏡折疊:


后視鏡.gif

而車窗升降與后視鏡旋轉(zhuǎn)存在不一樣的地方是,車窗的升降使用的是CABasicAnimation的平移而不是旋轉(zhuǎn)爪飘。

CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];//執(zhí)行平移動(dòng)畫

核心代碼是從哪里(fromValue)移動(dòng)到哪里(toValue):

/**
 降下車窗

 @param modelName 模型對(duì)象名稱
 @param duration 執(zhí)行周期
 */
- (void)downWindowsWithModelName:(NSString *)modelName offsetY:(CGFloat)offsetY duration:(NSTimeInterval)duration{
    for (SCNNode *windowNode in self.carModelNode.childNodes) {
        if ([windowNode.name isEqualToString:modelName]) {
            //創(chuàng)建自轉(zhuǎn)動(dòng)畫
            CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];//執(zhí)行平移動(dòng)畫
            animation.duration = duration;
            animation.toValue = [NSValue valueWithSCNVector3:SCNVector3Make(windowNode.position.x, windowNode.position.y-offsetY, windowNode.position.z)];
            animation.removedOnCompletion = NO;
            animation.fillMode = @"forwards";
            [windowNode addAnimation:animation forKey:@"window position"];
        }
    }
}

//升起車窗
- (void)upWindowsWithModelName:(NSString *)modelName offsetY:(CGFloat)offsetY duration:(NSTimeInterval)duration{
    for (SCNNode *windowNode in self.carModelNode.childNodes) {
        if ([windowNode.name isEqualToString:modelName]) {
            //創(chuàng)建自轉(zhuǎn)動(dòng)畫
            CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];//執(zhí)行平移動(dòng)畫
            animation.duration = duration;
            animation.fromValue = [NSValue valueWithSCNVector3:SCNVector3Make(windowNode.position.x, windowNode.position.y-offsetY, windowNode.position.z)];
            animation.toValue = [NSValue valueWithSCNVector3:SCNVector3Make(windowNode.position.x, windowNode.position.y, windowNode.position.z)];
            animation.removedOnCompletion = NO;
            animation.fillMode = @"forwards";
            [windowNode addAnimation:animation forKey:@"window position"];
        }
    }
}

升降車窗效果:


車窗升降.gif

本文Demo Git下載地址:https://github.com/heqican/ARKitCarModel

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末义起,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子师崎,更是在濱河造成了極大的恐慌默终,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異齐蔽,居然都是意外死亡两疚,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門含滴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)诱渤,“玉大人,你說(shuō)我怎么就攤上這事谈况∩酌溃” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵碑韵,是天一觀的道長(zhǎng)赡茸。 經(jīng)常有香客問(wèn)我,道長(zhǎng)祝闻,這世上最難降的妖魔是什么占卧? 我笑而不...
    開(kāi)封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮联喘,結(jié)果婚禮上华蜒,老公的妹妹穿的比我還像新娘。我一直安慰自己耸袜,他們只是感情好友多,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著堤框,像睡著了一般域滥。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蜈抓,一...
    開(kāi)封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天启绰,我揣著相機(jī)與錄音,去河邊找鬼沟使。 笑死委可,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的腊嗡。 我是一名探鬼主播着倾,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼燕少!你這毒婦竟也來(lái)了卡者?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤客们,失蹤者是張志新(化名)和其女友劉穎崇决,沒(méi)想到半個(gè)月后材诽,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡恒傻,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年脸侥,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片盈厘。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡睁枕,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出扑庞,到底是詐尸還是另有隱情譬重,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布罐氨,位于F島的核電站,受9級(jí)特大地震影響滩援,放射性物質(zhì)發(fā)生泄漏栅隐。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一玩徊、第九天 我趴在偏房一處隱蔽的房頂上張望租悄。 院中可真熱鬧,春花似錦恩袱、人聲如沸泣棋。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)潭辈。三九已至,卻和暖如春澈吨,著一層夾襖步出監(jiān)牢的瞬間把敢,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工谅辣, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留修赞,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓桑阶,卻偏偏與公主長(zhǎng)得像柏副,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蚣录,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344